diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000000000..6275ba74ef0ab9 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,6 @@ +# 2024-05-20 replace `refine'` with one underscore by `refine` (#13059) +7493b5f81b4c031b87877c5c124bea1ddc4e567d +# 2024-05-24 replace many `refine'` with `refine` (#13166) +fc48848e4374f13c796a7399bfccd2e228f776df +# 2025-11-19 move Mathlib to the module system (#31786) +6a54a80825b060ab20dc31751ebdce78b3a3b518 diff --git a/.github/actions/cache-trust-dispatch/action.yml b/.github/actions/cache-trust-dispatch/action.yml index 7e3811a969d2d9..3cb8f2f785ced0 100644 --- a/.github/actions/cache-trust-dispatch/action.yml +++ b/.github/actions/cache-trust-dispatch/action.yml @@ -66,6 +66,18 @@ runs: # be 403'd by Azure regardless. The dispatch exists so the workflow # does the right thing in the honest case; defence in depth is RBAC. + # Privileged containers (master, nightly-testing, pr-toolchain-tests) are + # writable only by a repo's own native CI, whose OIDC token is RBAC-scoped + # to match. A cross-repo pull request is built by build_fork.yml with + # fork-trust credentials whatever repo the PR head lives on (e.g. a `bump/*` + # branch on the mathlib4-nightly-testing fork), so it can only write + # `forks`. `GITHUB_REPOSITORY` is the repo the workflow runs as (the base + # repo for `pull_request_target`), i.e. the one whose credentials this job + # holds; when it differs from `$REPO`, the build is a fork and targets + # `forks` regardless of the head repo's own trust class. + if [ "$REPO" != "$GITHUB_REPOSITORY" ]; then + PRIMARY="forks" + else case "$REPO" in "leanprover-community/mathlib4") case "$BRANCH" in @@ -121,6 +133,7 @@ runs: PRIMARY="forks" ;; esac + fi # Per-commit cache namespace, only for fork-trust uploads. Closes the # within-fork temporal replay attack: each commit's CI run gets its diff --git a/.github/actions/get-mathlib-ci/README.md b/.github/actions/get-mathlib-ci/README.md index 77be1f2a6f15c9..96a1e071c4eb0f 100644 --- a/.github/actions/get-mathlib-ci/README.md +++ b/.github/actions/get-mathlib-ci/README.md @@ -25,7 +25,7 @@ then use the local action: ```yaml - name: Checkout local actions - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 diff --git a/.github/actions/get-mathlib-ci/action.yml b/.github/actions/get-mathlib-ci/action.yml index 0a0ba2f7ebcb9a..2cb8b3a47631b1 100644 --- a/.github/actions/get-mathlib-ci/action.yml +++ b/.github/actions/get-mathlib-ci/action.yml @@ -10,7 +10,7 @@ inputs: # Default pinned commit used by workflows unless they explicitly override. # Update this ref as needed to pick up changes to mathlib-ci scripts # This is also updated automatically by .github/workflows/update_dependencies.yml - default: 5aee9d4ce5a39050c72b4aa46015a824b0c189ac + default: 0cd6cbc879d9241f3b4f6cb7e8291e34128c5654 path: description: Checkout destination path. required: false @@ -33,7 +33,7 @@ runs: using: composite steps: - name: Get mathlib-ci - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: leanprover-community/mathlib-ci ref: ${{ inputs.ref }} diff --git a/.github/actions/get-tools/action.yml b/.github/actions/get-tools/action.yml index 6e195f21ea308e..185b6b03c4b069 100644 --- a/.github/actions/get-tools/action.yml +++ b/.github/actions/get-tools/action.yml @@ -139,7 +139,7 @@ runs: - name: Checkout tools branch (source build) if: ${{ steps.finalize.outputs.result == 'build' }} - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ inputs.tools_source_ref }} path: ${{ inputs.path }} diff --git a/.github/actions/setup-build-env/action.yml b/.github/actions/setup-build-env/action.yml new file mode 100644 index 00000000000000..e24ab2e2a92876 --- /dev/null +++ b/.github/actions/setup-build-env/action.yml @@ -0,0 +1,160 @@ +# Shared runner setup for the self-hosted `build` and `test_lint` jobs in +# build_template.yml. Both run on the same Hoskinson pool and need the same +# preparation before they diverge (build → build + stage, test_lint → +# fetch + test + lint): toolchain hygiene, jq, the cache-trust and get-tools +# wiring, the PR-branch checkout, the elan toolchain and LEAN_SRC_PATH +# environment, and the dependency download. +# +# The caller runs `Checkout local actions` (sparse `.github/actions` → +# `workflow-actions/`) before invoking this action: that checkout puts both this +# action and the sibling actions it calls (`cache-trust-dispatch`, `get-tools`) +# on disk under `workflow-actions/`, so the `uses: ./workflow-actions/...` paths +# below resolve relative to the workspace root, as in the calling workflow. +# +# Each `run:` step sets its own `shell:`, since composite steps do not inherit the +# job's landrun `defaults.run.shell`. +name: Set up build environment +description: Shared self-hosted runner setup for the build and test_lint jobs. + +inputs: + pr_branch_ref: + description: Git ref of the PR branch to check out and build. + required: true + tools_branch_ref: + description: Git ref to build the CI tools from when not using the prebuilt artifact. + required: false + default: '' + +runs: + using: composite + steps: + # Prune old toolchains from `~/.elan`. That directory is mounted from the host + # and shared across the ephemeral job containers, so toolchains accumulate and + # nothing else trims them (host-side housekeeping only trims the mathlib `.ltar` + # cache). Keep the 5 most recent plus `nightly`/`stable`. + - name: prune old toolchains + shell: bash # just deletes old files; safe to run outside landrun + run: | + # Make sure to delete both the `~/.elan/toolchains/X` directory and the `~/.elan/update-hashes/X` file. + # Skip symbolic links (`-type d`), the current directory (`! -name .`), and `nightly` and `stable`. + if cd ~/.elan/toolchains && find . -maxdepth 1 -type d ! -name . -print0 | xargs -0 ls -1td | grep -v 'nightly$' | grep -v 'stable$' | tail -n +6 | xargs -I {} sh -c 'echo {} && rm -rf "{}" && rm "../update-hashes/{}"'; then + : # Do nothing on success + else + : # Do nothing on failure, but suppress errors + fi + + # The Hoskinson runners may not have jq installed, so do that now. + - name: 'Setup jq' + uses: dcarbone/install-jq-action@4fcb5062d7ce9bc4382d1a352d19ba3ba2c317c1 # v4.0.1 + + # Compute the trust-classified container target and read fallback for this + # job. Sets MATHLIB_CACHE_FROM / MATHLIB_CACHE_PRIMARY in env so every + # subsequent `cache get` inherits them without per-call flag plumbing. Loaded + # from the trust-rooted `workflow-actions/` checkout, not the PR branch. + - name: Compute cache trust dispatch + uses: ./workflow-actions/.github/actions/cache-trust-dispatch + with: + repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }} + branch: ${{ github.head_ref || github.ref_name }} + head-sha: ${{ github.event.pull_request.head.sha || github.sha }} + + # Checkout the PR branch into a subdirectory. HEAD only (fetch-depth: 1) is + # enough: the cache is fetched HEAD-scoped and warmed from the master snapshot, + # so no parent-commit history is needed. This is untrusted (potentially fork) + # code we build, so don't leave the GITHUB_TOKEN in pr-branch/.git/config where + # that code could read it. + - name: Checkout PR branch + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ inputs.pr_branch_ref }} + fetch-depth: 1 + path: pr-branch + persist-credentials: false + # The build runs this fork PR code sandboxed (landrun) with no persisted + # credentials, so checking it out under pull_request_target is safe. + allow-unsafe-pr-checkout: true + + # Create empty directories so landrun doesn't complain. + - name: Create empty directories + shell: bash # We need to run this outside landrun, as it is a prerequisite for landrun! + run: | + mkdir -p pr-branch/.lake/ + mkdir -p .cache/mathlib/ + mkdir -p _work + + # NOTE: if you copy this, consider using `leanprover/lean-action` instead. + # We install manually, to avoid running lean outside landrun. + - name: install elan + shell: bash + run: | + set -o pipefail + curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh + chmod +x elan-init.sh + ./elan-init.sh -y --default-toolchain none + echo "$HOME/.elan/bin" >> "${GITHUB_PATH}" + + - name: set toolchain directory + shell: bash + run: | + cd pr-branch + # Get the lake binary path from elan and extract toolchain directory + LAKE_PATH=$(elan which lake) + echo "Lake path: $LAKE_PATH" + + # Extract the toolchain directory by removing /bin/lake from the end + TOOLCHAIN_DIR=$(dirname "$LAKE_PATH") + TOOLCHAIN_DIR=$(dirname "$TOOLCHAIN_DIR") + echo "Toolchain directory: $TOOLCHAIN_DIR" + + # Set it as an environment variable for subsequent steps + echo "TOOLCHAIN_DIR=$TOOLCHAIN_DIR" >> "$GITHUB_ENV" + + - name: set LEAN_SRC_PATH + shell: bash + run: | + cd pr-branch + + # Start with the base paths + LEAN_SRC_PATH=".:$TOOLCHAIN_DIR/src/lean/lake" + + # Extract package names from lake-manifest.json and validate them + # Only allow A-Z, a-z, 0-9, _, and - characters + # Build the LEAN_SRC_PATH by appending each validated package + PACKAGE_NAMES=$(jq -r '.packages[].name' lake-manifest.json) + for pkg in $PACKAGE_NAMES; do + if [[ "$pkg" =~ ^[A-Za-z0-9_-]+$ ]]; then + LEAN_SRC_PATH="$LEAN_SRC_PATH:.lake/packages/$pkg" + else + echo "Warning: Skipping invalid package name: $pkg" + fi + done + + echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" + + # Set it as an environment variable for subsequent steps + echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" >> "$GITHUB_ENV" + + # Populate `tools-branch/` with the trusted CI tooling (the `cache` binary and + # the `lake-build-*` helper scripts invoked by path). Fast path: download the + # prebuilt `tools-bin` artifact published from master (canonical mathlib4 only, + # and only when the branch under test doesn't change the cache tool — get-tools + # makes that comparison against `source_dir`). Source build otherwise. See the + # get-tools action for the full trust rationale. + - name: Get CI tools + uses: ./workflow-actions/.github/actions/get-tools + with: + use_artifact: ${{ inputs.tools_branch_ref == '' && github.repository == 'leanprover-community/mathlib4' }} + tools_source_ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }} + source_dir: ${{ github.event.pull_request.head.repo.fork != true && 'pr-branch' || '' }} + github_token: ${{ github.token }} + + - name: download dependencies + # We need network access to download dependencies. We run this inside + # landrun, but restrict disk access: + # - --rox access to `~/.elan` and `~/actions-runner/_work` (GitHub CI needs this) + # - --unrestricted-network as we need this to download dependencies + # - git needs read only access to `/etc`. + shell: landrun --unrestricted-network --rox /etc --rox /usr --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0} + run: | + cd pr-branch + lake env diff --git a/.github/workflows/PR_summary.yml b/.github/workflows/PR_summary.yml index e29630eafe1a55..c3350f3ed27589 100644 --- a/.github/workflows/PR_summary.yml +++ b/.github/workflows/PR_summary.yml @@ -17,16 +17,19 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 path: pr-branch # Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config. persist-credentials: false + # Only trusted base-repo scripts run against this checkout, so checking out + # fork PR code under pull_request_target is safe. + allow-unsafe-pr-checkout: true - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -64,7 +67,7 @@ jobs: fi - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: 3.12 @@ -230,6 +233,16 @@ jobs: echo "Compute technical debt changes" techDebtVar="$("${CI_SCRIPTS_DIR}/reporting/technical-debt-metrics.sh" pr_summary)" + # If the PR decreased technical debt, add the `tech debt` label. + # The metrics script emits a summary line "Decrease in tech debt:" for each + # level (strong/weak) that went down (and "Increase ..."/"No changes ..." otherwise), + # so matching either of those two lines means the PR reduced some tech debt. + if grep -qE "Decrease in (strong|weak) tech debt" <<< "${techDebtVar}" + then + printf $'Adding "tech debt" label to PR %s\n' "${PR}" + gh pr edit "${PR}" --add-label "tech debt" + fi + echo "Compute documentation reminder" workflowFilesChanged="$(grep '^\.github/workflows/' changed_files.txt || true)" if [ -n "${workflowFilesChanged}" ] diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index be2b1e7f6c7b39..0320272d125fcc 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: suggester / actionlint uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0 @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 # Using our fork's PR branch until upstream merges the improved error reporting: # https://github.com/zgosalvez/github-actions-ensure-sha-pinned-actions/pull/288 diff --git a/.github/workflows/add_label_from_diff.yaml b/.github/workflows/add_label_from_diff.yaml index a2257f8076023d..3738c9c273d046 100644 --- a/.github/workflows/add_label_from_diff.yaml +++ b/.github/workflows/add_label_from_diff.yaml @@ -22,7 +22,7 @@ jobs: if: github.repository == 'leanprover-community/mathlib4' steps: - name: Checkout master branch to build autolabel from - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master path: tools @@ -38,13 +38,16 @@ jobs: run: | lake build autolabel - name: Checkout branch to label - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.event.pull_request.head.sha || github.sha }} fetch-depth: 0 path: pr-branch # Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config. persist-credentials: false + # autolabel is built from the trusted base checkout and only reads these files, + # so checking out fork PR code under pull_request_target is safe. + allow-unsafe-pr-checkout: true - name: Run autolabel working-directory: pr-branch run: | diff --git a/.github/workflows/build_template.yml b/.github/workflows/build_template.yml index 511e19544781d5..6e333b0394ffa4 100644 --- a/.github/workflows/build_template.yml +++ b/.github/workflows/build_template.yml @@ -57,11 +57,11 @@ jobs: archive-outcome: ${{ steps.archive.outcome }} counterexamples-outcome: ${{ steps.counterexamples.outcome }} cache-staging-has-files: ${{ steps.cache_staging_check.outputs.has_files }} - lint-outcome: ${{ steps.lint.outcome }} mk_all-outcome: ${{ steps.mk_all.outcome }} noisy-outcome: ${{ steps.noisy.outcome }} # shake-outcome: ${{ steps.shake.outcome }} - test-outcome: ${{ steps.test.outcome }} + # `test`/`lint` outcomes are exposed by the `test_lint` job (and consumed by + # its nightly-testing comment step). defaults: # On Hoskinson runners, landrun is already installed. run: # note that .pr-branch/.lake must be created in a step below before we use this shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0} @@ -77,72 +77,24 @@ jobs: shell: bash # there is no script body, so this is safe to "run" outside landrun. run: | # We just populate the env vars for this step to make them viewable in the logs - - name: cleanup - shell: bash # This *just* deletes old files, so is safe to run outside landrun. - run: | - if ! find . -mindepth 1 -exec rm -rf -- {} +; then - echo "ERROR: Initial cleanup failed, waiting 5 seconds and retrying..." - sleep 5 - find . -mindepth 1 -exec rm -rf -- {} + - fi - # Delete all but the 5 most recent toolchains. - # Make sure to delete both the `~/.elan/toolchains/X` directory and the `~/.elan/update-hashes/X` file. - # Skip symbolic links (`-type d`), the current directory (`! -name .`), and `nightly` and `stable`. - if cd ~/.elan/toolchains && find . -maxdepth 1 -type d ! -name . -print0 | xargs -0 ls -1td | grep -v 'nightly$' | grep -v 'stable$' | tail -n +6 | xargs -I {} sh -c 'echo {} && rm -rf "{}" && rm "../update-hashes/{}"'; then - : # Do nothing on success - else - : # Do nothing on failure, but suppress errors - fi - - # The Hoskinson runners may not have jq installed, so do that now. - - name: 'Setup jq' - uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0 - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 sparse-checkout: .github/actions path: workflow-actions - # We need to 'duplicate' this step below because GitHub Actions does not let a `uses:` step - # conditionally omit a single `with:` key, and passing `ref: ''` would override - # the composite action's default to the empty string instead of falling back to it - # Note only one of the two below will actually run in any given run - - name: Get mathlib-ci - if: ${{ inputs.mathlib_ci_ref == '' }} - uses: ./workflow-actions/.github/actions/get-mathlib-ci - - - name: Get mathlib-ci - if: ${{ inputs.mathlib_ci_ref != '' }} - uses: ./workflow-actions/.github/actions/get-mathlib-ci - with: - ref: ${{ inputs.mathlib_ci_ref }} - - # Compute the trust-classified container target and read fallback - # for this job. Sets MATHLIB_CACHE_FROM / MATHLIB_CACHE_PRIMARY in - # env so every subsequent `cache get` in this job inherits them - # without per-call flag plumbing. Loaded from master via the sparse - # `workflow-actions/` checkout above, not from the PR branch — this - # keeps the trust policy out of fork-controllable file paths. - - name: Compute cache trust dispatch - uses: ./workflow-actions/.github/actions/cache-trust-dispatch + # Shared self-hosted setup (toolchain prune, jq, cache-trust dispatch, + # PR-branch checkout, elan + toolchain/LEAN_SRC_PATH env, CI tools, dependency + # download); see the action for per-step rationale. It is loaded from the + # `Checkout local actions` checkout above, which therefore runs first. + - name: Set up build environment + uses: ./workflow-actions/.github/actions/setup-build-env with: - repo: ${{ github.event.pull_request.head.repo.full_name || github.repository }} - branch: ${{ github.head_ref || github.ref_name }} - head-sha: ${{ github.event.pull_request.head.sha || github.sha }} - - # Checkout the PR branch into a subdirectory - - name: Checkout PR branch - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - ref: ${{ inputs.pr_branch_ref }} - fetch-depth: 2 # we may fetch cache from the commit before this one (or earlier) - path: pr-branch - # This is an untrusted (potentially fork) checkout whose code we build. - # Don't leave the GITHUB_TOKEN in pr-branch/.git/config, where that code could read it. - persist-credentials: false + pr_branch_ref: ${{ inputs.pr_branch_ref }} + tools_branch_ref: ${{ inputs.tools_branch_ref }} # TEMPORARY (cache storage-layout migration, PR #40035): remove once # active branches have merged the new cache tool. @@ -194,103 +146,6 @@ jobs: cp ../lean-toolchain . echo "lean-toolchain copied successfully to DownstreamTest." - # Create empty directories so landrun doesn't complain - - name: Create empty directories - shell: bash # We need to run this outside landrun, as it is a prerequisite for landrun! - run: | - mkdir -p pr-branch/.lake/ - mkdir -p .cache/mathlib/ - mkdir -p _work - - # NOTE: if you copy this, consider using `leanprover/lean-action` instead. - # We install manually, to avoid running lean outside landrun. - - name: install elan - shell: bash - run: | - set -o pipefail - curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh - chmod +x elan-init.sh - ./elan-init.sh -y --default-toolchain none - echo "$HOME/.elan/bin" >> "${GITHUB_PATH}" - - - name: set toolchain directory - shell: bash - run: | - cd pr-branch - # Get the lake binary path from elan and extract toolchain directory - LAKE_PATH=$(elan which lake) - echo "Lake path: $LAKE_PATH" - - # Extract the toolchain directory by removing /bin/lake from the end - TOOLCHAIN_DIR=$(dirname "$LAKE_PATH") - TOOLCHAIN_DIR=$(dirname "$TOOLCHAIN_DIR") - echo "Toolchain directory: $TOOLCHAIN_DIR" - - # Set it as an environment variable for subsequent steps - echo "TOOLCHAIN_DIR=$TOOLCHAIN_DIR" >> "$GITHUB_ENV" - - - name: set LEAN_SRC_PATH - shell: bash - run: | - cd pr-branch - - # Start with the base paths - LEAN_SRC_PATH=".:$TOOLCHAIN_DIR/src/lean/lake" - - # Extract package names from lake-manifest.json and validate them - # Only allow A-Z, a-z, 0-9, _, and - characters - # Build the LEAN_SRC_PATH by appending each validated package - PACKAGE_NAMES=$(jq -r '.packages[].name' lake-manifest.json) - for pkg in $PACKAGE_NAMES; do - if [[ "$pkg" =~ ^[A-Za-z0-9_-]+$ ]]; then - LEAN_SRC_PATH="$LEAN_SRC_PATH:.lake/packages/$pkg" - else - echo "Warning: Skipping invalid package name: $pkg" - fi - done - - echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" - - # Set it as an environment variable for subsequent steps - echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" >> "$GITHUB_ENV" - - # Populate `tools-branch/` with the trusted CI tooling (the `cache` binary and - # the `lake-build-*` helper scripts invoked by path below). - # - # - Fast path: download the prebuilt `tools-bin` artifact published from - # `master` by `publish_tools.yml` — only on canonical mathlib4, and only - # when the branch under test doesn't change the cache tool. `get-tools` - # makes that comparison itself against `source_dir` (the already-checked- - # out `pr-branch`), so the tool-source path list lives in the action, not - # duplicated here. - # - Source build: from `tools_source_ref`, pointed at the branch *under - # test* (`pr_branch_ref`) — bors `staging`/`trying`, in-repo dev branches, - # and the nightly-testing repo (which has no `master` branch) — so the - # built tool matches the tree. Fork PRs keep `master` and skip the - # comparison (empty `source_dir`): they run master's `build_template` via - # `pull_request_target`, never this one, and their untrusted tool must - # never be built/run with cache credentials (bors exercises tool changes - # in-repo instead). - - name: Get CI tools - uses: ./workflow-actions/.github/actions/get-tools - with: - use_artifact: ${{ inputs.tools_branch_ref == '' && github.repository == 'leanprover-community/mathlib4' }} - tools_source_ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }} - source_dir: ${{ github.event.pull_request.head.repo.fork != true && 'pr-branch' || '' }} - github_token: ${{ github.token }} - - - name: download dependencies - # We need network access to download dependencies - # We run this inside landrun, but restrict disk access. - # Landrun argument notes: - # - we give --rox access to `~/.elan` and `~/actions-runner/_work` (GitHub CI needs this) - # - we give --unrestricted-network as we need this to download dependencies - # - git needs read only access to `/etc`. - shell: landrun --unrestricted-network --rox /etc --rox /usr --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0} - run: | - cd pr-branch - lake env - - name: validate lake-manifest.json inputRevs # Only enforce this on the main mathlib4 repository, not on nightly-testing if: github.repository == 'leanprover-community/mathlib4' && github.ref_name != 'nightly-testing' @@ -335,7 +190,7 @@ jobs: 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 + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after. linters: lean @@ -356,7 +211,7 @@ jobs: ../tools-branch/scripts/lake-build-with-retry.sh Mathlib # results of build at pr-branch/.lake/build_summary_Mathlib.json - name: end gh-problem-match-wrap for build step - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: remove linters: lean @@ -397,6 +252,24 @@ jobs: ../tools-branch/scripts/lake-build-with-retry.sh Counterexamples # results of build at pr-branch/.lake/build_summary_Counterexamples.json + # Runs in the build job because it only needs the freshly-built Mathlib/ + # Archive/Counterexamples oleans, which are present here; keeping it in + # `build` also spares `test_lint` from fetching Archive/Counterexamples. + - name: check for noisy stdout lines + id: noisy + run: | + cd pr-branch + buildMsgs="$( + ## we exploit `lake`s replay feature: since the cache is present, running + ## `lake build` will reproduce all the outputs without having to recompute + lake build -q --iofail Mathlib Archive Counterexamples + )" + if [ -n "${buildMsgs}" ] + then + printf $'%s\n' "${buildMsgs}" + exit 1 + fi + - name: prepare staging directory if: ${{ always() && (steps.build.outcome == 'success' || steps.build.outcome == 'failure' || steps.build.outcome == 'cancelled') }} shell: bash @@ -443,6 +316,8 @@ jobs: with: name: cache-staging path: cache-staging/ + # The `.ltar` files are already zstd-compressed; recompressing wastes CPU. + compression-level: 0 # Prune to this commit's `.ltar` set so the published snapshot is exactly master's # current cache (the local dir also holds the previous snapshot it warmed from). @@ -489,24 +364,128 @@ jobs: echo "'mk_all --check' passed successfully." fi + test_lint: + name: Test and lint + needs: [build] + # Runs `test` and `lint` on the same self-hosted pool as `build`, but as a + # separate job so it runs in parallel with the GitHub-hosted post-build chain + # (upload_cache -> post_steps) rather than ahead of it. `build` and `test_lint` + # run sequentially on the pool, so peak per-run concurrency is unchanged; the + # only cost is a second setup prefix on a fresh runner. + # + # It does not depend on `upload_cache`: it reconstitutes this run's oleans + # without the paid Azure round-trip, by downloading the `cache-staging` artifact + # `build` produces (this commit's freshly-built delta) and warming the rest from + # the master `cache-snapshot` via the `get-cache` action. The `verify the Mathlib + # cache is complete` step below fails loudly if that reconstitution is incomplete, + # rather than silently rebuilding under landrun (which has no network). + # + # Gating: runs when `build` succeeded or failed (not cancelled/skipped). Running + # on build FAILURE too means `lint` still reports results on a failed build, + # best-effort over whatever oleans were built. The per-step conditions below + # match: `test` only on a clean build, `lint` on success-or-failure. + if: ${{ always() && (needs.build.result == 'success' || needs.build.result == 'failure') }} + runs-on: ${{ inputs.runs_on }} + outputs: + lint-outcome: ${{ steps.lint.outcome }} + test-outcome: ${{ steps.test.outcome }} + defaults: # On Hoskinson runners, landrun is already installed. + run: # note that pr-branch/.lake must be created in a step below before we use this + shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI -- bash -euxo pipefail {0} + steps: + - name: Checkout local actions + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + ref: ${{ github.workflow_sha }} + fetch-depth: 1 + sparse-checkout: .github/actions + path: workflow-actions + + # Shared self-hosted setup (see the action). + - name: Set up build environment + uses: ./workflow-actions/.github/actions/setup-build-env + with: + pr_branch_ref: ${{ inputs.pr_branch_ref }} + tools_branch_ref: ${{ inputs.tools_branch_ref }} + + # mathlib-ci provides the scripts for the lean-pr-testing comment at the end of + # this job, which runs only on the nightly-testing repo — so check it out only + # there. Written twice because a `uses:` step cannot conditionally omit the + # single `ref` key (`ref: ''` would override the action's pinned default). + - name: Get mathlib-ci + if: ${{ github.repository == 'leanprover-community/mathlib4-nightly-testing' && inputs.mathlib_ci_ref == '' }} + uses: ./workflow-actions/.github/actions/get-mathlib-ci + + - name: Get mathlib-ci + if: ${{ github.repository == 'leanprover-community/mathlib4-nightly-testing' && inputs.mathlib_ci_ref != '' }} + uses: ./workflow-actions/.github/actions/get-mathlib-ci + with: + ref: ${{ inputs.mathlib_ci_ref }} + + # Pre-populate the local cache with this commit's freshly-built delta: the + # `cache-staging` artifact `build` produces holds its newly-built `.ltar` — + # the oleans not already in the master snapshot. `get-cache` below then warms + # the unchanged bulk from the master `cache-snapshot` and unpacks everything, + # so the bulk of the cache is not re-downloaded from paid Azure storage. + # Gated on `cache-staging-has-files`: a full cache hit stages nothing (no + # artifact is uploaded), and `get-cache` alone then covers the commit. + - name: download cache staging artifact + if: ${{ needs.build.outputs.cache-staging-has-files == 'true' }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: cache-staging + path: /home/lean/.cache/mathlib + + # Warm the unchanged bulk from the master `cache-snapshot` and fetch/unpack + # this commit's oleans. The staged `.ltar` placed above are already local, so + # `get-cache`'s `cache get` decompresses them rather than re-downloading; only + # anything still missing is pulled from Azure. + - name: Get cache + uses: ./workflow-actions/.github/actions/get-cache + with: + working_directory: pr-branch + cache_bin: ../tools-branch/.lake/build/bin/cache + + - name: verify the Mathlib cache is complete + # Enforced only when the build succeeded (then the cache must be complete): + # fail fast rather than silently trying to rebuild under landrun (which has + # no network). On a failed build the cache is expected to be partial, so this + # is skipped and `lint` runs best-effort below. + if: ${{ needs.build.outputs.build-outcome == 'success' }} + # TODO: remove if there are no issues + continue-on-error: true + run: | + cd pr-branch + lake build --no-build --rehash -v Mathlib + + # `test` runs only on a clean build: `build` itself, the `mk_all` check, and + # the archive/counterexamples builds all succeeded. The condition reads those + # from the build job's outputs, and the problem-matcher wrap is gated to match. - name: begin gh-problem-match-wrap for test step - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }} + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after. linters: lean - name: test mathlib + if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }} id: test run: | cd pr-branch ../tools-branch/scripts/lake-build-wrapper.py .lake/build_summary_MathlibTest.json lake --iofail test - name: end gh-problem-match-wrap for test step - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + if: ${{ needs.build.outputs.build-outcome == 'success' && needs.build.outputs.mk_all-outcome == 'success' && needs.build.outputs.archive-outcome == 'success' && needs.build.outputs.counterexamples-outcome == 'success' }} + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: remove linters: lean + # `lint` runs on a build that succeeded or failed (not cancelled). On a failed + # build it lints best-effort over whatever oleans the cache has, so partial + # lint feedback is still reported. The problem-matcher wrap is gated to match. - name: begin gh-problem-match-wrap for shake and lint steps - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }} + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: add # In order to be able to run a multiline script, we need to add/remove the problem matcher before and after. linters: gcc @@ -519,7 +498,7 @@ jobs: # cd pr-branch # env LEAN_ABORT_ON_PANIC=1 lake exe shake --gh-style - name: lint mathlib - if: ${{ always() && steps.build.outcome == 'success' || steps.build.outcome == 'failure' }} + if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }} id: lint timeout-minutes: 40 run: | @@ -558,29 +537,12 @@ jobs: done - name: end gh-problem-match-wrap for shake and lint steps - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + if: ${{ always() && (needs.build.outputs.build-outcome == 'success' || needs.build.outputs.build-outcome == 'failure') }} + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: remove linters: gcc - - name: check for noisy stdout lines - id: noisy - run: | - cd pr-branch - buildMsgs="$( - ## we exploit `lake`s replay feature: since the cache is present, running - ## `lake build` will reproduce all the outputs without having to recompute - lake build Mathlib Archive Counterexamples | - ## we filter out the output lines that begin with `✔ [xx/yy]`, where xx, yy - ## are either numbers or ?, and the "Build completed successfully." message. - ## We keep the rest, which are actual outputs of the files - awk '!($0 ~ "^\\s*✔ \\[[?0-9]*/[?0-9]*\\]" || $0 ~ "^Build completed successfully( \\([0-9]+ jobs\\))?\\.?$"){ print $0 }')" - if [ -n "${buildMsgs}" ] - then - printf $'%s\n' "${buildMsgs}" - exit 1 - fi - # Generate a fresh token just before posting comments. # GitHub App tokens expire after 1 hour, and the build can take longer than that. - name: Generate lean-pr-testing app token @@ -602,10 +564,12 @@ jobs: TOKEN: ${{ steps.lean-pr-testing-token.outputs.token }} GITHUB_CONTEXT: ${{ toJson(github) }} WORKFLOW_URL: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} - BUILD_OUTCOME: ${{ steps.build.outcome }} - NOISY_OUTCOME: ${{ steps.noisy.outcome }} - ARCHIVE_OUTCOME: ${{ steps.archive.outcome }} - COUNTEREXAMPLES_OUTCOME: ${{ steps.counterexamples.outcome }} + # build/archive/counterexamples/noisy outcomes come from the `build` job's + # outputs; test/lint are produced by this job's own steps. + BUILD_OUTCOME: ${{ needs.build.outputs.build-outcome }} + NOISY_OUTCOME: ${{ needs.build.outputs.noisy-outcome }} + ARCHIVE_OUTCOME: ${{ needs.build.outputs.archive-outcome }} + COUNTEREXAMPLES_OUTCOME: ${{ needs.build.outputs.counterexamples-outcome }} LINT_OUTCOME: ${{ steps.lint.outcome }} TEST_OUTCOME: ${{ steps.test.outcome }} NIGHTLY_TESTING_REPO: leanprover-community/mathlib4-nightly-testing @@ -640,7 +604,7 @@ jobs: # `build_template` via `pull_request_target`, never this one — so # `pr_branch_ref` is always a trusted ref here. Fork PRs keep `master`. - name: Checkout tools branch - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ inputs.tools_branch_ref != '' && inputs.tools_branch_ref || (github.event.pull_request.head.repo.fork && 'master' || inputs.pr_branch_ref) }} fetch-depth: 1 @@ -694,21 +658,8 @@ jobs: # $MATHLIB_CACHE_PRIMARY is set by the `Compute cache trust dispatch` # step above, from the shared composite action that owns the (repo, # branch) → container mapping for both this job and the read-side - # jobs (build, post_steps). - # Dual-write to the legacy `mathlib4` container first, then the - # primary. Only master CI dual-writes: older cache tools read only - # `legacy`, so it must stay fresh for them, while forks and nightly - # never write `legacy` (keeping low-trust artifacts out of what those - # readers trust). Writing `legacy` first keeps it a superset of - # `master` for as long as we dual-write: with `set -e`, a failed - # legacy write aborts the step before `master` gets artifacts that - # `legacy` lacks. (`put-staged` exits non-zero on real upload - # failures; already-present 409/412 blobs are not failures.) - if [ "$MATHLIB_CACHE_PRIMARY" = "master" ]; then - echo "Dual-writing to legacy container first (keeps legacy a superset of master)..." - lake env "$CACHE_BIN" put-staged --container=legacy --staging-dir="cache-staging" --repo="$REPO" - fi - + # jobs (build, post_steps). Each job writes only into its own trust-level + # container. echo "Uploading cache to Azure (container: $MATHLIB_CACHE_PRIMARY)..." lake env "$CACHE_BIN" put-staged --container="$MATHLIB_CACHE_PRIMARY" --staging-dir="cache-staging" --repo="$REPO" @@ -723,17 +674,20 @@ jobs: contents: read steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ inputs.pr_branch_ref }} # Untrusted (potentially fork) checkout: don't persist the GITHUB_TOKEN into its .git/config. persist-credentials: false + # This job runs with only `contents: read` and no persisted credentials, + # so checking out fork PR code under pull_request_target is safe. + allow-unsafe-pr-checkout: true # Sparse-checkout master's `.github/actions/` so the trust dispatch # below loads from a trust-rooted source, not from PR-branch-controlled # content. Mirrors the `Checkout local actions` step in the `build` job. - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -797,7 +751,7 @@ jobs: lake exe graph - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -847,6 +801,9 @@ jobs: run: | lake env lean scripts/create_deprecated_modules.lean lake env lean scripts/autolabel.lean + # Executing this also runs the export and writes a (gitignored) `crossrefs.json`, + # which doubles as a smoke test of the `export_crossrefs.yml` workflow. + lake env lean scripts/export_crossrefs.lean lake exe check_title_labels --labels "t-algebra" "feat: dummy PR for testing" - name: build everything @@ -881,8 +838,8 @@ jobs: final: name: Post-CI job # ensure that this runs iff direct dependencies succeeded even if transitive dependencies were skipped - if: ${{ always() && inputs.run_post_ci && needs.style_lint.result == 'success' && needs.build.result == 'success' && needs.post_steps.result == 'success' }} - needs: [style_lint, build, post_steps] + if: ${{ always() && inputs.run_post_ci && needs.style_lint.result == 'success' && needs.build.result == 'success' && needs.test_lint.result == 'success' && needs.post_steps.result == 'success' }} + needs: [style_lint, build, test_lint, post_steps] runs-on: ubuntu-latest steps: - id: PR diff --git a/.github/workflows/cache_test.yml b/.github/workflows/cache_test.yml index ecf1b87a9cf864..69a965e6678d62 100644 --- a/.github/workflows/cache_test.yml +++ b/.github/workflows/cache_test.yml @@ -1,8 +1,9 @@ -# Runs the cache tool's unit tests (`lake exe cache-test`) on PRs that touch -# the cache code. The suite is deliberately network-free (see the module -# docstring in `Cache/Test.lean`), so the job only needs the toolchain and the -# package dependencies' sources — not a Mathlib build — and finishes in a few -# minutes on a hosted runner. +# Runs the cache tool's unit tests (`lake exe cache-test`) on Linux and Windows +# for PRs that touch the cache code. The suite is deliberately network-free (see +# the module docstring in `Cache/Test.lean`), so the job only needs the toolchain +# and the package dependencies' sources — not a Mathlib build — and finishes in a +# few minutes on a hosted runner. The Windows job guards the tool's cross-platform +# behavior, such as the null device the marker probe and tests discard output to. name: cache tests on: @@ -13,6 +14,8 @@ on: # a bump can break the tool's compilation even with `Cache/` untouched. - 'lakefile.lean' - 'lean-toolchain' + # Re-run when the workflow itself changes. + - '.github/workflows/cache_test.yml' concurrency: group: cache-test-${{ github.ref }} @@ -24,20 +27,38 @@ permissions: jobs: cache-test: if: github.repository == 'leanprover-community/mathlib4' - runs-on: ubuntu-latest + strategy: + # Report both platforms even if one fails, so a Windows-only break is + # never hidden by a Linux failure (or vice versa). + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} + # `leanprover/lean-action` installs elan from a bash step and adds it to the + # path in that shell's form, so the `lake` steps below must also run under + # bash (available on Windows runners as Git Bash) to find it. + defaults: + run: + shell: bash steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: install elan - run: | - set -o pipefail - curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh - chmod +x elan-init.sh - ./elan-init.sh -y --default-toolchain none - echo "$HOME/.elan/bin" >> "${GITHUB_PATH}" + # Install elan and the toolchain cross-platform. Build/test/lint, the + # Mathlib cache, and the GitHub cache are all disabled, so this is a + # toolchain-only setup; the `lake` steps below then build and run just the + # `cache-test` target. (The GitHub cache cannot be written from fork PRs + # and buys little for this small build, so it stays off.) + - uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0 + with: + auto-config: "false" + build: "false" + test: "false" + lint: "false" + use-mathlib-cache: "false" + use-github-cache: "false" - name: build cache-test run: lake build cache-test - name: run cache tests - run: .lake/build/bin/cache-test + run: lake exe cache-test diff --git a/.github/workflows/check_pr_titles.yaml b/.github/workflows/check_pr_titles.yaml index edbe9058ecd2a8..7d995d59b2c69c 100644 --- a/.github/workflows/check_pr_titles.yaml +++ b/.github/workflows/check_pr_titles.yaml @@ -19,7 +19,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master - name: Configure Lean diff --git a/.github/workflows/commit_verification.yml b/.github/workflows/commit_verification.yml index b117d8f7235ac6..d62c03386f5ba2 100644 --- a/.github/workflows/commit_verification.yml +++ b/.github/workflows/commit_verification.yml @@ -33,14 +33,14 @@ jobs: # This is a quick check to avoid unnecessary runs steps: - name: Checkout PR head - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Checkout the actual PR head, not the merge commit GitHub creates ref: ${{ github.event.pull_request.head.sha }} # Fetch full history to access all PR commits fetch-depth: 0 - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -111,7 +111,7 @@ jobs: }' > bridge-outputs.json - name: Emit bridge artifact - uses: leanprover-community/privilege-escalation-bridge/emit@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/emit@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: artifact: workflow-data outputs_file: bridge-outputs.json diff --git a/.github/workflows/commit_verification_wf_run.yml b/.github/workflows/commit_verification_wf_run.yml index 622820aae6f452..dec43f0e0a8bdb 100644 --- a/.github/workflows/commit_verification_wf_run.yml +++ b/.github/workflows/commit_verification_wf_run.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Consume bridge artifact id: bridge - uses: leanprover-community/privilege-escalation-bridge/consume@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/consume@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: token: ${{ github.token }} artifact: workflow-data diff --git a/.github/workflows/daily-master-tag.yml b/.github/workflows/daily-master-tag.yml index 83007e40169d7e..96aeefcb3382ae 100644 --- a/.github/workflows/daily-master-tag.yml +++ b/.github/workflows/daily-master-tag.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'leanprover-community/mathlib4' steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml index cd75a4c5e8c103..3865c77fd5e2b4 100644 --- a/.github/workflows/daily.yml +++ b/.github/workflows/daily.yml @@ -31,7 +31,7 @@ jobs: steps: # Checkout repository, so that we can fetch tags to decide which branch we want. - name: Checkout branch or tag - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Fetch latest tags (if nightly) if: matrix.branch_type == 'nightly' @@ -52,7 +52,7 @@ jobs: # Checkout the branch or tag we want to test. - name: Checkout branch or tag - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }} ref: ${{ env.BRANCH_REF }} @@ -82,7 +82,7 @@ jobs: branch_type: [master, nightly] steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Get job status and URLs id: get-status @@ -122,7 +122,7 @@ jobs: - name: Post success message for leanchecker on Zulip if: steps.get-status.outputs.job_conclusion == 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -135,7 +135,7 @@ jobs: - name: Post failure / cancelled message for leanchecker on Zulip if: steps.get-status.outputs.job_conclusion != 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -156,7 +156,7 @@ jobs: steps: # Checkout repository, so that we can fetch tags to decide which branch we want. - name: Checkout branch or tag - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Fetch latest tags (if nightly) if: matrix.branch_type == 'nightly' @@ -177,7 +177,7 @@ jobs: # Checkout the branch or tag we want to test. - name: Checkout branch or tag - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }} ref: ${{ env.BRANCH_REF }} @@ -205,7 +205,7 @@ jobs: branch_type: [master, nightly] steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Get job status and URLs id: get-status @@ -245,7 +245,7 @@ jobs: - name: Post success message for mathlib_test_executable on Zulip if: steps.get-status.outputs.job_conclusion == 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -258,7 +258,7 @@ jobs: - name: Post failure / cancelled message for mathlib_test_executable on Zulip if: steps.get-status.outputs.job_conclusion != 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -279,7 +279,7 @@ jobs: steps: # Checkout repository, so that we can fetch tags to decide which branch we want. - name: Checkout branch or tag - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Fetch latest tags (if nightly) if: matrix.branch_type == 'nightly' @@ -300,7 +300,7 @@ jobs: # Checkout the branch or tag we want to test. - name: Checkout branch or tag - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ matrix.branch_type == 'nightly' && 'leanprover-community/mathlib4-nightly-testing' || github.repository }} ref: ${{ env.BRANCH_REF }} @@ -370,7 +370,7 @@ jobs: branch_type: [master, nightly] steps: - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Get job status and URLs id: get-status @@ -410,7 +410,7 @@ jobs: - name: Post success message for nanoda on Zulip if: steps.get-status.outputs.job_conclusion == 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -423,7 +423,7 @@ jobs: - name: Post failure / cancelled message for nanoda on Zulip if: steps.get-status.outputs.job_conclusion != 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/decls-diff.yml b/.github/workflows/decls-diff.yml index aca4899a3af3ad..d4405ca5107524 100644 --- a/.github/workflows/decls-diff.yml +++ b/.github/workflows/decls-diff.yml @@ -58,10 +58,13 @@ jobs: } | tee -a "$GITHUB_OUTPUT" - name: Checkout new commit - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ steps.meta.outputs.new-sha }} fetch-depth: 0 + # `new-sha` is fork PR code already built by build_fork.yml; allow the + # fork checkout under this workflow_run. + allow-unsafe-pr-checkout: true - name: Resolve merge-base against master id: resolve @@ -136,7 +139,7 @@ jobs: # Tooling is checked out unconditionally: the patcher (from CI_SCRIPTS_DIR) # is needed on the cache-miss path too, to post the warning notice. - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 diff --git a/.github/workflows/docker_build.yml b/.github/workflows/docker_build.yml index b8b6c0fa81ac43..3e0c28a07aaa7c 100644 --- a/.github/workflows/docker_build.yml +++ b/.github/workflows/docker_build.yml @@ -27,7 +27,7 @@ jobs: steps: # documentation at # https://docs.github.com/en/actions/use-cases-and-examples/publishing-packages/publishing-docker-images - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Log in to the container registry uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 with: @@ -59,7 +59,7 @@ jobs: tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} - name: Generate artifact attestation - uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0 + uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1 with: subject-name: ${{ env.REGISTRY }}/${{ env.REPO_NAME }}/${{ matrix.image }} subject-digest: ${{ steps.push.outputs.digest }} diff --git a/.github/workflows/export_crossrefs.yml b/.github/workflows/export_crossrefs.yml new file mode 100644 index 00000000000000..fa12a8e457553d --- /dev/null +++ b/.github/workflows/export_crossrefs.yml @@ -0,0 +1,161 @@ +name: Export cross-reference data + +# Generates a JSON dictionary of every declaration tagged with `@[wikidata]`, +# `@[stacks]`, or `@[kerodon]` (name, file, line, and the referenced ids) and +# pushes it to a versioned repository (`CROSSREFS_REPO`), where it is publicly +# available at `https://raw.githubusercontent.com//master/crossrefs.json`. +# +# The data is read from the fully-imported `Mathlib` environment by +# `scripts/export_crossrefs.lean`, like the `#stacks_tags` command. + +on: + workflow_run: + workflows: ["continuous integration"] + branches: [master] + types: [completed] + workflow_dispatch: + inputs: + dry_run: + description: "Dry run: mint the app token and verify push permission, but don't push to the crossrefs repo" + type: boolean + default: true + +concurrency: + group: export-crossrefs + cancel-in-progress: true + +permissions: + contents: read + # Required for the token-minting action to request a GitHub OIDC token, + # which it exchanges (via Azure Key Vault) for a GitHub App installation token. + id-token: write + +env: + CROSSREFS_REPO: leanprover-community/crossref-exports + BUILT_SHA: ${{ github.event.workflow_run.head_sha || github.sha }} + +jobs: + export: + name: Export cross-reference JSON + runs-on: ubuntu-latest + environment: + name: crossref-exports + deployment: false + if: >- + github.repository == 'leanprover-community/mathlib4' && + (github.event_name == 'workflow_dispatch' + || github.event.workflow_run.conclusion == 'success') + steps: + - name: Checkout the built commit + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + persist-credentials: false + # The exact commit CI validated; for manual runs, the dispatched ref. + ref: ${{ env.BUILT_SHA }} + + - name: Configure Lean and fetch the Mathlib cache + uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0 + with: + auto-config: false + use-github-cache: false + use-mathlib-cache: true + reinstall-transient-toolchain: true + + - name: Generate crossrefs.json + env: + CROSSREFS_OUT: crossrefs.json + CROSSREFS_COMMIT: ${{ env.BUILT_SHA }} + run: lake env lean scripts/export_crossrefs.lean + + - name: Sanity-check the output + run: | + jq -e '.entries | length > 0' crossrefs.json > /dev/null + echo "Exported $(jq '.entries | length' crossrefs.json) entries." + + # Split `owner/repo` so the token can be scoped to the crossrefs repo. The + # app is installed on the crossrefs repo's owner, not on this repository, + # so the token-minting action must resolve the installation from there. + - name: Resolve crossrefs repository owner and name + id: repo + run: | + echo "owner=${CROSSREFS_REPO%%/*}" >> "$GITHUB_OUTPUT" + echo "name=${CROSSREFS_REPO#*/}" >> "$GITHUB_OUTPUT" + + # Mint a short-lived GitHub App installation token scoped to the crossrefs + # repo, instead of relying on a long-lived personal access token. + - name: Generate app token + id: app-token + uses: leanprover-community/mathlib-ci/.github/actions/azure-create-github-app-token@3bb576208589a435eeaeac9b144a1b7c3e948760 + with: + app-id: ${{ secrets.MATHLIB_CROSSREFS_APP_ID }} + key-vault-name: ${{ vars.MATHLIB_AZ_KEY_VAULT_NAME }} + key-name: crossref-exports-app-pk + azure-client-id: ${{ vars.GH_APP_AZURE_CLIENT_ID_CROSSREFS }} + azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }} + owner: ${{ steps.repo.outputs.owner }} + repositories: ${{ steps.repo.outputs.name }} + + - name: Push to the crossrefs repository + env: + CROSSREFS_PUSH_TOKEN: ${{ steps.app-token.outputs.token }} + # On manual runs this defaults to true (see the workflow_dispatch + # inputs); on the automatic workflow_run trigger `inputs` is empty, so + # this is false and the push happens for real. + DRY_RUN: ${{ inputs.dry_run || false }} + run: | + git clone --depth 1 \ + "https://x-access-token:${CROSSREFS_PUSH_TOKEN}@github.com/${CROSSREFS_REPO}.git" \ + crossrefs-repo + cd crossrefs-repo + git config user.name "leanprover-community-bot" + git config user.email "leanprover-community-bot@users.noreply.github.com" + # Commit only when the entries themselves change. The `generated` + # timestamp and source `commit` change on every run, so comparing the + # whole file would produce a commit even when nothing of substance did. + # Consequence: the published `generated`/`commit` fields record when the + # entries last changed, not the latest commit the export ran against, so + # consumers should not read `generated` as a "data freshness" signal. + # Read each file fully into a string and compare; using `diff -q` on two + # `jq` process substitutions makes `diff` close the pipes at the first + # difference, which leaves the `jq` writers with a SIGPIPE ("broken pipe") + # and noisy spurious errors in the log. + if [ -f crossrefs.json ] && \ + [ "$(jq -Sc '.entries' crossrefs.json)" = "$(jq -Sc '.entries' ../crossrefs.json)" ]; then + echo "No change in entries." + changed=false + else + cp ../crossrefs.json crossrefs.json + git add crossrefs.json + git commit -m "Update crossrefs (mathlib4@${BUILT_SHA})" + changed=true + fi + + if [ "${DRY_RUN}" = "true" ]; then + # Verify the minted token can actually push, without mutating the + # remote: `git push --dry-run` performs the authenticated + # git-receive-pack handshake (which GitHub gates on write access) but + # never sends the update. If nothing changed, add a throwaway local + # commit so there is a ref update to negotiate. + if [ "${changed}" != "true" ]; then + git commit --allow-empty -m "Dry-run push permission check (never sent)" + fi + echo "Dry run: verifying push permission against ${CROSSREFS_REPO} without pushing." + git push --dry-run + elif [ "${changed}" = "true" ]; then + git push + else + echo "No change in entries; nothing to commit." + fi + + - name: Post failure message on Zulip + if: failure() + uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + with: + api-key: ${{ secrets.ZULIP_API_KEY }} + email: 'github-mathlib4-bot@leanprover.zulipchat.com' + organization-url: 'https://leanprover.zulipchat.com' + to: 'nightly-testing-mathlib' + type: 'stream' + topic: 'crossrefs export failure' + content: | + ❌ Cross-reference export [failed](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) on ${{ env.BUILT_SHA }} diff --git a/.github/workflows/lake_cache_shadow.yml b/.github/workflows/lake_cache_shadow.yml index ceb83aa283ec17..68a7710e79bc1c 100644 --- a/.github/workflows/lake_cache_shadow.yml +++ b/.github/workflows/lake_cache_shadow.yml @@ -62,6 +62,10 @@ concurrency: group: lake-cache-shadow-${{ inputs.mathlib_ref || 'master' }} cancel-in-progress: false +defaults: + run: + shell: bash -euo pipefail {0} + env: # Scope prefix for all puts and gets in this workflow. Namespaces the # workflow's artifacts within the cache bucket. @@ -87,20 +91,20 @@ jobs: shell: landrun --rox /usr --ro /etc/timezone --rw /dev --rox /home/lean/.elan --rox /home/lean/actions-runner/_work --rox /home/lean/.cache/mathlib/ --rw pr-branch/.lake/ --env PATH --env HOME --env GITHUB_OUTPUT --env CI --env LAKE_CACHE_DIR --env LAKE_NO_CACHE --env STAGE_TARGETS --env SHADOW_SCOPE -- bash -euxo pipefail {0} steps: - name: job info - shell: bash + shell: bash -euo pipefail {0} run: echo "::notice::Lake cache shadow on ref ${{ inputs.mathlib_ref || 'master' }} run ${{ github.run_id }}" - name: Setup jq - uses: dcarbone/install-jq-action@b7ef57d46ece78760b4019dbc4080a1ba2a40b45 # v3.2.0 + uses: dcarbone/install-jq-action@4fcb5062d7ce9bc4382d1a352d19ba3ba2c317c1 # v4.0.1 - name: Checkout tools branch - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master path: tools-branch - name: Checkout mathlib (pr-branch) - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ inputs.mathlib_ref || 'master' }} fetch-depth: 2 @@ -108,7 +112,7 @@ jobs: - name: Resolve sha & toolchain id: resolve - shell: bash + shell: bash -euo pipefail {0} run: | cd pr-branch SHA="$(git rev-parse HEAD)" @@ -117,23 +121,22 @@ jobs: echo "toolchain=$TC" >> "$GITHUB_OUTPUT" - name: Create empty directories (landrun prerequisites) - shell: bash + shell: bash -euo pipefail {0} run: | mkdir -p pr-branch/.lake/ mkdir -p .cache/mathlib/ mkdir -p _work - name: install elan - shell: bash + shell: bash -euo pipefail {0} run: | - set -o pipefail curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh chmod +x elan-init.sh ./elan-init.sh -y --default-toolchain none echo "$HOME/.elan/bin" >> "${GITHUB_PATH}" - name: set toolchain directory - shell: bash + shell: bash -euo pipefail {0} run: | cd pr-branch LAKE_PATH=$(elan which lake) @@ -141,7 +144,7 @@ jobs: echo "TOOLCHAIN_DIR=$TOOLCHAIN_DIR" >> "$GITHUB_ENV" - name: set LEAN_SRC_PATH - shell: bash + shell: bash -euo pipefail {0} run: | cd pr-branch LEAN_SRC_PATH=".:$TOOLCHAIN_DIR/src/lean/lake" @@ -154,7 +157,7 @@ jobs: echo "LEAN_SRC_PATH=$LEAN_SRC_PATH" >> "$GITHUB_ENV" - name: build tools-branch tools - shell: bash + shell: bash -euo pipefail {0} run: | cd tools-branch lake build cache @@ -166,13 +169,13 @@ jobs: lake env - name: Hydrate .lake/build via legacy cache - shell: bash + shell: bash -euo pipefail {0} run: | cd pr-branch ../tools-branch/.lake/build/bin/cache get - name: Patch lakefile to enable Lake's artifact cache - shell: bash + shell: bash -euo pipefail {0} run: | cd pr-branch if grep -q 'enableArtifactCache' lakefile.lean; then @@ -183,12 +186,10 @@ jobs: from pathlib import Path p = Path("lakefile.lean") src = p.read_text() - marker = ' testDriver := "MathlibTest"' + marker = ' restoreAllArtifacts := true' inject = ( - ' testDriver := "MathlibTest"\n' - ' fixedToolchain := true\n' - ' enableArtifactCache := true\n' - ' restoreAllArtifacts := true' + ' restoreAllArtifacts := true\n' + ' enableArtifactCache := true' ) if marker not in src: raise SystemExit("lakefile.lean shape changed; update the shadow patch in lake_cache_shadow.yml") @@ -228,7 +229,7 @@ jobs: # write to it. Same pattern as build_template.yml uses for the legacy # cache-staging dir. - name: Create staging directory - shell: bash + shell: bash -euo pipefail {0} run: mkdir -p lake-cache-staging - name: Stage cache for upload @@ -245,7 +246,7 @@ jobs: # free-tier / lifecycle budget. Logs the size first, so a trip still tells # us how big the build got. - name: Guard upload size (bail if staging > 1 GB) - shell: bash + shell: bash -euo pipefail {0} run: | MAX=$((1 * 1000 * 1000 * 1000)) # 1 GB — ~2.5x the measured ~389 MB full build (runaway guard) BYTES=$(du -sb lake-cache-staging | cut -f1) @@ -277,16 +278,14 @@ jobs: LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT }} steps: - name: Checkout mathlib (for lean-toolchain pin) - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ needs.build_and_stage.outputs.sha }} path: pr-branch fetch-depth: 1 - name: install elan + matching lake - shell: bash run: | - set -o pipefail curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh chmod +x elan-init.sh ./elan-init.sh -y --default-toolchain none @@ -336,7 +335,8 @@ jobs: base="${AUTH%/artifacts}/analysis" sha="${{ needs.build_and_stage.outputs.sha }}" sig=(--aws-sigv4 aws:amz:auto:s3 --user "$LAKE_CACHE_KEY") - grep -oE 'uploaded artifact [0-9a-f]+' /tmp/put.log | awk '{print $3}' | sort -u > /tmp/today.txt + # grep exits 1 on no match (handled by the total==0 branch); tolerate it under pipefail. + grep -oE 'uploaded artifact [0-9a-f]+' /tmp/put.log | awk '{print $3}' | sort -u > /tmp/today.txt || true total=$(wc -l < /tmp/today.txt) if [ "$total" -eq 0 ]; then echo "::warning::no uploaded artifacts parsed from put.log — skipping carryover (manifest/pointer left unchanged)" @@ -410,7 +410,7 @@ jobs: LAKE_CACHE_REVISION_ENDPOINT: ${{ vars.LAKE_CACHE_REVISION_ENDPOINT_PUBLIC }} steps: - name: Checkout mathlib - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ needs.build_and_stage.outputs.sha }} path: pr-branch @@ -418,7 +418,6 @@ jobs: - name: install elan run: | - set -o pipefail curl -o elan-init.sh -sSfL https://elan.lean-lang.org/elan-init.sh chmod +x elan-init.sh ./elan-init.sh -y --default-toolchain none @@ -437,12 +436,10 @@ jobs: from pathlib import Path p = Path("lakefile.lean") src = p.read_text() - marker = ' testDriver := "MathlibTest"' + marker = ' restoreAllArtifacts := true' inject = ( - ' testDriver := "MathlibTest"\n' - ' fixedToolchain := true\n' - ' enableArtifactCache := true\n' - ' restoreAllArtifacts := true' + ' restoreAllArtifacts := true\n' + ' enableArtifactCache := true' ) if marker not in src: raise SystemExit("lakefile.lean shape changed") @@ -458,7 +455,8 @@ jobs: # Artifacts fetched here = the root-package outputs served from cache # (a non-verbose build replays them silently). Hand the count to the # provenance step. - FETCHED=$(find ~/.elan/toolchains -name '*.ltar' 2>/dev/null | wc -l) + # `|| true`: an informational count must not trip pipefail if find fails. + FETCHED=$(find ~/.elan/toolchains -name '*.ltar' 2>/dev/null | wc -l || true) echo "ltar count after get: $FETCHED" echo "FETCHED_ARTIFACTS=$FETCHED" >> "$GITHUB_ENV" @@ -514,7 +512,6 @@ jobs: steps: - name: Compose Zulip message id: compose - shell: bash env: BUILD: ${{ needs.build_and_stage.result }} UPLOAD: ${{ needs.upload.result }} @@ -547,7 +544,7 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Send to Zulip - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/latest_import.yml b/.github/workflows/latest_import.yml index eeb9d92bda388a..8aacc26bd9584d 100644 --- a/.github/workflows/latest_import.yml +++ b/.github/workflows/latest_import.yml @@ -26,10 +26,10 @@ jobs: : # Do nothing on failure, but suppress errors fi - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -68,7 +68,7 @@ jobs: - name: build mathlib id: build - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: linters: lean run: | @@ -86,7 +86,7 @@ jobs: tee "$GITHUB_OUTPUT" - name: Post output to Zulip - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/lint_and_suggest_pr.yml b/.github/workflows/lint_and_suggest_pr.yml index 903e230f59a719..3f350e695ca77c 100644 --- a/.github/workflows/lint_and_suggest_pr.yml +++ b/.github/workflows/lint_and_suggest_pr.yml @@ -13,3 +13,13 @@ jobs: with: mode: suggest lint-bib-file: true + - name: All Mathlib files have a .lean or .md extension + run: | + exc="$(git ls-files 'Mathlib/*' | grep -v "\.\(lean\|md\)$" || true)" + printf '%s\n' "${exc}" + if [ -n "${exc}" ]; + then + printf $'There are files whose extension is neither \'.lean\' nor \'.md\' in Mathlib/:\n\n%s\n' "${exc}" + printf $'\nPlease, make sure that this is really what you want!\n' + exit 1 + fi diff --git a/.github/workflows/long_file_report.yml b/.github/workflows/long_file_report.yml index 034ae752cd59f4..03e0a37bf3400c 100644 --- a/.github/workflows/long_file_report.yml +++ b/.github/workflows/long_file_report.yml @@ -12,10 +12,10 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -30,7 +30,7 @@ jobs: printf $'summary<- # coarse prefilter to avoid running on irrelevant comments/reviews github.repository == 'leanprover-community/mathlib4' && @@ -38,8 +37,7 @@ jobs: ( contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors merge') || contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors d') || - contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors r') || - contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'Build failed:') + contains(format('{0}{1}', github.event.comment.body, github.event.review.body), 'bors r') ) steps: - name: Find bors merge/delegate @@ -51,26 +49,20 @@ jobs: # for debugging, we print some information printf '%s' "${COMMENT}" | hexdump -cC printf 'Comment:"%s"\n' "${COMMENT}" - if [ "${AUTHOR}" == 'mathlib-bors[bot]' ] && [ "${HAS_DELEGATED_LABEL}" == 'true' ] - then - m_or_d="$(printf '%s' "${COMMENT}" | - sed -n 's=^Build failed:=delegated=p' | head -1)" - else - m_or_d="$(printf '%s' "${COMMENT}" | - sed -n 's=^bors *\(merge\|r+\) *$=ready-to-merge=p; s=^bors *\(delegate\|d+\|d\=\).*=delegated=p' | head -1)" - fi - - remove_labels="$(printf '%s' "${COMMENT}" | - sed -n 's=^bors *\(merge\|r\|d\)- *$=remove-labels=p' | head -1)" + # `mOrD` records which command this is ("ready-to-merge" or "delegated"). + # It no longer drives any label: bors now manages the `ready-to-merge` and + # `delegated` lifecycle labels itself (see the `[labels]` table in bors.toml). + # It is kept only to select the Zulip emoji and to gate the + # awaiting-author/maintainer-merge cleanup in `maintainer_bors_wf_run.yml`. + m_or_d="$(printf '%s' "${COMMENT}" | + sed -n 's=^bors *\(merge\|r+\) *$=ready-to-merge=p; s=^bors *\(delegate\|d+\|d\=\).*=delegated=p' | head -1)" printf $'"bors delegate" or "bors merge" found? \'%s\'\n' "${m_or_d}" - printf $'"bors r-" or "bors d-" found? \'%s\'\n' "${remove_labels}" printf $'AUTHOR: \'%s\'\n' "${AUTHOR}" printf $'PR_NUMBER: \'%s\'\n' "${PR_NUMBER}" printf $'%s' "${PR_NUMBER}" | hexdump -cC printf $'mOrD=%s\n' "${m_or_d}" >> "${GITHUB_OUTPUT}" - printf $'removeLabels=%s\n' "${remove_labels}" >> "${GITHUB_OUTPUT}" if [ "${AUTHOR}" == 'leanprover-community-mathlib4-bot' ] || [ "${AUTHOR}" == 'leanprover-community-bot-assistant' ] || [ "${AUTHOR}" == 'mathlib-bors[bot]' ] || @@ -83,22 +75,20 @@ jobs: printf $'bot=false\n' >> "${GITHUB_OUTPUT}" fi - - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' || ! steps.merge_or_delegate.outputs.removeLabels == '' }} + - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' }} name: Prepare bridge outputs run: | jq -n \ --arg bot "${{ steps.merge_or_delegate.outputs.bot }}" \ - --arg removeLabels "${{ steps.merge_or_delegate.outputs.removeLabels }}" \ --arg mOrD "${{ steps.merge_or_delegate.outputs.mOrD }}" \ '{ bot: $bot, - removeLabels: $removeLabels, mOrD: $mOrD, }' > bridge-outputs.json - - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' || ! steps.merge_or_delegate.outputs.removeLabels == '' }} + - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' }} name: Emit bridge artifact - uses: leanprover-community/privilege-escalation-bridge/emit@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/emit@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: artifact: workflow-data outputs_file: bridge-outputs.json diff --git a/.github/workflows/maintainer_bors_wf_run.yml b/.github/workflows/maintainer_bors_wf_run.yml index d67089472846df..b257787bb9fda2 100644 --- a/.github/workflows/maintainer_bors_wf_run.yml +++ b/.github/workflows/maintainer_bors_wf_run.yml @@ -1,8 +1,8 @@ -name: Add "ready-to-merge" and "delegated" label (workflow_run) +name: Bors merge/delegate follow-up (workflow_run) on: workflow_run: - workflows: ['Add "ready-to-merge" and "delegated" label'] + workflows: ['Bors merge/delegate follow-up'] types: - completed @@ -12,8 +12,8 @@ permissions: jobs: - add_ready_to_merge_label: - name: Add ready-to-merge or delegated label + bors_command_followup: + name: Bors merge/delegate follow-up runs-on: ubuntu-latest if: ${{ github.repository == 'leanprover-community/mathlib4' && github.event.workflow_run.conclusion == 'success' }} permissions: @@ -23,10 +23,10 @@ jobs: steps: - name: Consume bridge artifact id: bridge - uses: leanprover-community/privilege-escalation-bridge/consume@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/consume@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: artifact: workflow-data - source_workflow: Add "ready-to-merge" and "delegated" label + source_workflow: Bors merge/delegate follow-up require_event: issue_comment,pull_request_review,pull_request_review_comment fail_on_missing: false token: ${{ github.token }} @@ -34,7 +34,6 @@ jobs: author=event.comment.user.login|event.review.user.login pr_number=meta.pr_number bot=outputs.bot - removeLabels=outputs.removeLabels mOrD=outputs.mOrD - name: Download legacy artifact (fallback) @@ -62,7 +61,6 @@ jobs: echo "author=$(jq -r '.author // empty' "${data_file}")" echo "pr_number=$(jq -r '.pr_number // empty' "${data_file}")" echo "bot=$(jq -r '.bot // empty' "${data_file}")" - echo "removeLabels=$(jq -r '.removeLabels // .remove_labels // empty' "${data_file}")" echo "mOrD=$(jq -r '.mOrD // .m_or_d // empty' "${data_file}")" } | tee -a "$GITHUB_OUTPUT" @@ -73,7 +71,6 @@ jobs: INPUT_AUTHOR: ${{ steps.bridge.outputs.author }}${{ steps.legacy.outputs.author }} INPUT_PR_NUMBER: ${{ steps.bridge.outputs.pr_number }}${{ steps.legacy.outputs.pr_number }} INPUT_BOT: ${{ steps.bridge.outputs.bot }}${{ steps.legacy.outputs.bot }} - INPUT_REMOVE_LABELS: ${{ steps.bridge.outputs.removeLabels }}${{ steps.legacy.outputs.removeLabels }} INPUT_MORD: ${{ steps.bridge.outputs.mOrD }}${{ steps.legacy.outputs.mOrD }} INPUT_SOURCE: ${{ steps.bridge.outputs.pr_number != '' && 'bridge' || (steps.legacy.outputs.pr_number != '' && 'legacy' || 'none') }} run: | @@ -81,7 +78,6 @@ jobs: echo "author=${INPUT_AUTHOR}" echo "pr_number=${INPUT_PR_NUMBER}" echo "bot=${INPUT_BOT}" - echo "removeLabels=${INPUT_REMOVE_LABELS}" echo "mOrD=${INPUT_MORD}" echo "input_source=${INPUT_SOURCE}" } | tee -a "$GITHUB_OUTPUT" @@ -94,7 +90,7 @@ jobs: - name: Check whether user is a mathlib admin id: user_permission - if: ${{ ! steps.inputs.outputs.mOrD == '' || ! steps.inputs.outputs.removeLabels == '' }} + if: ${{ ! steps.inputs.outputs.mOrD == '' }} uses: actions-cool/check-user-permission@c21884f3dda18dafc2f8b402fe807ccc9ec1aa5e # v2.4.0 with: username: ${{ steps.inputs.outputs.author }} @@ -103,7 +99,7 @@ jobs: - name: Generate app token id: app-token uses: leanprover-community/mathlib-ci/.github/actions/azure-create-github-app-token@3bb576208589a435eeaeac9b144a1b7c3e948760 - if: ${{ ! steps.inputs.outputs.mOrD == '' || ! steps.inputs.outputs.removeLabels == '' }} + if: ${{ ! steps.inputs.outputs.mOrD == '' }} with: app-id: ${{ secrets.MATHLIB_TRIAGE_APP_ID }} key-vault-name: ${{ vars.MATHLIB_AZ_KEY_VAULT_NAME }} @@ -111,23 +107,11 @@ jobs: azure-client-id: ${{ vars.GH_APP_AZURE_CLIENT_ID_TRIAGE }} azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }} - - name: Add ready-to-merge or delegated label - id: add_label - if: ${{ ! steps.inputs.outputs.mOrD == '' && - ( steps.user_permission.outputs.require-result == 'true' || - steps.inputs.outputs.bot == 'true' ) }} - uses: octokit/request-action@b91aabaa861c777dcdb14e2387e30eddf04619ae # v3.0.0 - with: - route: POST /repos/:repository/issues/:issue_number/labels - # Unexpected input warning from the following is expected: - # https://github.com/octokit/request-action?tab=readme-ov-file#warnings - repository: ${{ github.repository }} - issue_number: ${{ steps.inputs.outputs.pr_number }} - labels: '["${{ steps.inputs.outputs.mOrD }}"]' - env: - # This token is masked by the token minting action and will not be logged accidentally - GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} - + # The `ready-to-merge` / `delegated` lifecycle labels are managed by bors + # itself (see the `[labels]` table in bors.toml); this workflow no longer + # adds or removes them. It still removes the `awaiting-author` and + # `maintainer-merge` labels and updates the Zulip emoji on a merge/delegate + # command. - if: ${{ ! steps.inputs.outputs.mOrD == '' && ( steps.user_permission.outputs.require-result == 'true' || steps.inputs.outputs.bot == 'true' ) }} @@ -142,22 +126,11 @@ jobs: --header 'authorization: Bearer ${{ steps.app-token.outputs.token }}' done - - name: On bors r/d-, remove ready-to-merge or delegated label - if: ${{ ! steps.inputs.outputs.removeLabels == '' && steps.user_permission.outputs.require-result == 'true' }} - # 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 - run: | - for label in ready-to-merge delegated; do - curl --request DELETE \ - --url "https://api.github.com/repos/${{ github.repository }}/issues/${{ steps.inputs.outputs.pr_number }}/labels/${label}" \ - --header 'authorization: Bearer ${{ steps.app-token.outputs.token }}' - done - - name: Checkout local actions if: ${{ ! steps.inputs.outputs.mOrD == '' && ( steps.user_permission.outputs.require-result == 'true' || steps.inputs.outputs.bot == 'true' ) }} - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -173,7 +146,7 @@ jobs: if: ${{ ! steps.inputs.outputs.mOrD == '' && ( steps.user_permission.outputs.require-result == 'true' || steps.inputs.outputs.bot == 'true' ) }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.x' diff --git a/.github/workflows/maintainer_merge.yml b/.github/workflows/maintainer_merge.yml index 518228ae09ac43..47fac3e957522b 100644 --- a/.github/workflows/maintainer_merge.yml +++ b/.github/workflows/maintainer_merge.yml @@ -72,7 +72,7 @@ jobs: - if: ${{ ! steps.merge_or_delegate.outputs.mOrD == '' }} name: Emit bridge artifact - uses: leanprover-community/privilege-escalation-bridge/emit@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/emit@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: artifact: workflow-data outputs_file: bridge-outputs.json diff --git a/.github/workflows/maintainer_merge_wf_run.yml b/.github/workflows/maintainer_merge_wf_run.yml index 81ed3364171722..14a3673cffc39c 100644 --- a/.github/workflows/maintainer_merge_wf_run.yml +++ b/.github/workflows/maintainer_merge_wf_run.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Consume bridge artifact id: bridge - uses: leanprover-community/privilege-escalation-bridge/consume@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/consume@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: artifact: workflow-data source_workflow: Maintainer merge @@ -163,7 +163,7 @@ jobs: - name: Checkout local actions if: ${{ steps.authorized.outputs.authorized == 'true' }} - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -213,7 +213,7 @@ jobs: - name: Send message on Zulip if: ${{ steps.authorized.outputs.authorized == 'true' }} continue-on-error: true - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/nightly_bump_and_merge.yml b/.github/workflows/nightly_bump_and_merge.yml index 5418ef6c3ba2fc..1a344d7a134d50 100644 --- a/.github/workflows/nightly_bump_and_merge.yml +++ b/.github/workflows/nightly_bump_and_merge.yml @@ -39,14 +39,14 @@ jobs: # This token is masked by the token minting action and will not be logged accidentally. - name: Checkout nightly-testing branch - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: nightly-testing fetch-depth: 0 # Fetch all branches and history token: ${{ steps.app-token.outputs.token }} - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -371,7 +371,7 @@ jobs: - name: Send message on Zulip if: steps.commit-bump.outputs.bumped == 'true' && env.branches_exist == 'true' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/nightly_detect_failure.yml b/.github/workflows/nightly_detect_failure.yml index 8a2010877288a3..f887e968f7413c 100644 --- a/.github/workflows/nightly_detect_failure.yml +++ b/.github/workflows/nightly_detect_failure.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Send message on Zulip - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -122,7 +122,7 @@ jobs: # This token is masked by the token minting action and will not be logged accidentally. - name: Checkout code - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # Pin to the SHA whose CI just succeeded, not the current tip of `nightly-testing`, # which may have advanced while CI was running. Without this, the tag and @@ -216,7 +216,7 @@ jobs: # The create-github-app-token README states that this token is masked and will not be logged accidentally. - name: Checkout Lean repository if: steps.tag.outputs.is_nightly == 'true' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: leanprover/lean4 token: ${{ steps.lean-pr-testing-token.outputs.token }} @@ -362,7 +362,7 @@ jobs: - name: Send warning message on Zulip if pattern doesn't match if: steps.tag.outputs.is_nightly == 'true' && failure() && steps.bump_version.outcome == 'failure' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -418,7 +418,7 @@ jobs: azure-client-id: ${{ vars.GH_APP_AZURE_CLIENT_ID_NIGHTLY_TESTING }} azure-tenant-id: ${{ secrets.LPC_AZ_TENANT_ID }} - name: Checkout Mathlib4 repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false' with: ref: nightly-testing # checkout nightly-testing branch (shouldn't matter which) @@ -427,7 +427,7 @@ jobs: - name: Checkout local actions if: steps.tag.outputs.is_nightly == 'true' && steps.check_branch.outputs.result == 'false' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -546,7 +546,7 @@ jobs: # own dedicated ⚠️ warning step above (otherwise we would double-post). - name: Report housekeeping failure to Zulip if: failure() && steps.bump_version.outcome != 'failure' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/nightly_merge_master.yml b/.github/workflows/nightly_merge_master.yml index e371f9e9b4cd10..39725897ccfa3e 100644 --- a/.github/workflows/nightly_merge_master.yml +++ b/.github/workflows/nightly_merge_master.yml @@ -27,7 +27,7 @@ jobs: # This token is masked by the token minting action and will not be logged accidentally. - name: Checkout nightly-testing from fork - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: leanprover-community/mathlib4-nightly-testing ref: nightly-testing diff --git a/.github/workflows/nolints.yml b/.github/workflows/nolints.yml index 9716c0526a6205..e1addcf674c915 100644 --- a/.github/workflows/nolints.yml +++ b/.github/workflows/nolints.yml @@ -14,7 +14,7 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Configure Lean uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0 diff --git a/.github/workflows/olean_report.yaml b/.github/workflows/olean_report.yaml index 4c3fb07cece678..aaa1ecb18c9d66 100644 --- a/.github/workflows/olean_report.yaml +++ b/.github/workflows/olean_report.yaml @@ -48,7 +48,7 @@ jobs: - name: Checkout local actions if: steps.check_trigger.outputs.triggered == 'true' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -82,7 +82,7 @@ jobs: # We fetch full depth so that we can compute the merge base. - name: Checkout PR head if: steps.check_trigger.outputs.triggered == 'true' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ github.repository }} ref: refs/pull/${{ github.event.issue.number }}/head @@ -95,7 +95,7 @@ jobs: # here so that a single binary can fetch oleans for both checkouts. - name: Checkout tools branch (master) if: steps.check_trigger.outputs.triggered == 'true' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ github.repository }} ref: master @@ -117,7 +117,7 @@ jobs: - name: Checkout merge base if: steps.check_trigger.outputs.triggered == 'true' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: repository: ${{ github.repository }} ref: ${{ steps.merge_base.outputs.sha }} @@ -286,7 +286,7 @@ jobs: - name: Emit bridge artifact if: steps.check_trigger.outputs.triggered == 'true' - uses: leanprover-community/privilege-escalation-bridge/emit@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/emit@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: artifact: workflow-data outputs_file: bridge-outputs.json diff --git a/.github/workflows/olean_report_wf_run.yaml b/.github/workflows/olean_report_wf_run.yaml index 3df323a179e3e5..22c7e44d05887b 100644 --- a/.github/workflows/olean_report_wf_run.yaml +++ b/.github/workflows/olean_report_wf_run.yaml @@ -25,7 +25,7 @@ jobs: steps: - name: Consume bridge artifact id: bridge - uses: leanprover-community/privilege-escalation-bridge/consume@f5dfe313a79647c07315b451b2dc2a81a161a50d # v1.2.0 + uses: leanprover-community/privilege-escalation-bridge/consume@ea7d63d1c8ece92a8e89b6a6d8fda40603167a91 # v1.3.0 with: token: ${{ github.token }} artifact: workflow-data diff --git a/.github/workflows/pr_check_downstream.yml b/.github/workflows/pr_check_downstream.yml index ba7756d551c3e2..b099ba488b7c4b 100644 --- a/.github/workflows/pr_check_downstream.yml +++ b/.github/workflows/pr_check_downstream.yml @@ -170,7 +170,7 @@ jobs: # via a sparse checkout so we can run it; we never need the # rest of the mathlib4 working tree on this runner. - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 15f6cbbb285324..6e5aaceeb2a4ce 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -20,8 +20,8 @@ jobs: main: runs-on: ubuntu-latest steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: 3.x - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 diff --git a/.github/workflows/publish_tools.yml b/.github/workflows/publish_tools.yml index 028cd32bdc2fb6..999cd7a8c676e9 100644 --- a/.github/workflows/publish_tools.yml +++ b/.github/workflows/publish_tools.yml @@ -40,7 +40,7 @@ jobs: # Build under `tools-branch/`, the same directory `build_template.yml` unpacks # the tools into, in case the build bakes its own location into the binary. - name: Checkout master - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master path: tools-branch diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7d7e06d1e90976..b35bdf8ac3a34a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: if: github.repository == 'leanprover-community/mathlib4' steps: - name: Create GitHub Release - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 + uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1 with: prerelease: ${{ contains(github.ref, 'rc') }} make_latest: ${{ !contains(github.ref, 'rc') }} diff --git a/.github/workflows/remove_deprecated_decls.yml b/.github/workflows/remove_deprecated_decls.yml index 553f222523dcfa..fb2a6fc98f37d8 100644 --- a/.github/workflows/remove_deprecated_decls.yml +++ b/.github/workflows/remove_deprecated_decls.yml @@ -112,7 +112,7 @@ jobs: echo "from_date=$from_date" >> "$GITHUB_OUTPUT" echo "to_date=$to_date" >> "$GITHUB_OUTPUT" - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Configure Lean uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0 @@ -195,7 +195,7 @@ jobs: - name: Send Zulip message (blocked by open PR) if: steps.find-pr.outputs.pr_found == 'true' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -242,7 +242,7 @@ jobs: - name: Send Zulip message (success) if: steps.pr.outcome == 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -256,7 +256,7 @@ jobs: - name: Send Zulip message (failure) if: ${{ toJson(inputs.dry_run) != 'true' && failure() }} - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/rm_set_option.yml b/.github/workflows/rm_set_option.yml index 780af2bdfb07d5..8b21d6604ec0d3 100644 --- a/.github/workflows/rm_set_option.yml +++ b/.github/workflows/rm_set_option.yml @@ -31,7 +31,7 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Configure Lean uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0 @@ -42,7 +42,7 @@ jobs: - name: Restore progress cache if: toJson(inputs.dry_run) != 'true' - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: scripts/.rm_set_option_progress.jsonl # Use run_id in restore-keys so we always pick up the most recent cache @@ -75,7 +75,7 @@ jobs: # Skip on dry-run (no files are modified) and if the progress file was # cleaned up (meaning the run completed fully). if: always() && toJson(inputs.dry_run) != 'true' && hashFiles('scripts/.rm_set_option_progress.jsonl') != '' - uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: scripts/.rm_set_option_progress.jsonl key: rm-set-option-progress-${{ hashFiles('lean-toolchain') }}-${{ github.run_id }} @@ -131,7 +131,7 @@ jobs: - name: Send Zulip message (success) if: steps.pr.outcome == 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -144,7 +144,7 @@ jobs: - name: Send Zulip message (failure) if: ${{ toJson(inputs.dry_run) != 'true' && failure() }} - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/shake.yaml b/.github/workflows/shake.yaml index 1bd86610cc16ca..47c77eb10be4d4 100644 --- a/.github/workflows/shake.yaml +++ b/.github/workflows/shake.yaml @@ -26,7 +26,7 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Configure Lean uses: leanprover/lean-action@38fbc41a8c28c4cbaec22d7f7de508ec2e7c0dd9 # v1.5.0 @@ -123,7 +123,7 @@ jobs: - name: Send Zulip message (success) if: steps.pr.outcome == 'success' - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -139,7 +139,7 @@ jobs: - name: Send Zulip message (failure) if: ${{ toJson(inputs.dry_run) != 'true' && failure() }} - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/technical_debt_metrics.yml b/.github/workflows/technical_debt_metrics.yml index 506f868b7a4a1e..6d102888c511c9 100644 --- a/.github/workflows/technical_debt_metrics.yml +++ b/.github/workflows/technical_debt_metrics.yml @@ -12,12 +12,12 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # checkout all history so that we can compare across commits fetch-depth: 0 - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -32,7 +32,7 @@ jobs: printf $'summary<> "$GITHUB_OUTPUT" - name: Post output to Zulip - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/update_dependencies.yml b/.github/workflows/update_dependencies.yml index b6213824640b54..5974d14528aab1 100644 --- a/.github/workflows/update_dependencies.yml +++ b/.github/workflows/update_dependencies.yml @@ -27,7 +27,7 @@ jobs: # This token is masked by the token minting action and will not be logged accidentally. - name: Checkout repository - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 token: ${{ steps.app-token.outputs.token }} @@ -206,7 +206,7 @@ jobs: - name: Send Zulip message (failure) if: ${{ failure() }} - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/update_dependencies_zulip.yml b/.github/workflows/update_dependencies_zulip.yml index 5f440f10c8aac1..f0520210cfa84d 100644 --- a/.github/workflows/update_dependencies_zulip.yml +++ b/.github/workflows/update_dependencies_zulip.yml @@ -17,13 +17,13 @@ jobs: id-token: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 2 # Need previous commit for diff - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -33,7 +33,7 @@ jobs: uses: ./workflow-actions/.github/actions/get-mathlib-ci - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.x' @@ -89,7 +89,7 @@ jobs: return output; - name: Send Zulip message - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' @@ -108,12 +108,12 @@ jobs: id-token: write steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 2 # Need previous commit for diff - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -123,7 +123,7 @@ jobs: uses: ./workflow-actions/.github/actions/get-mathlib-ci - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.x' @@ -168,7 +168,7 @@ jobs: - name: Send Zulip message if: ${{ steps.construct_message.outputs.result != '' }} - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/validate_mathlib_ci_paths.yml b/.github/workflows/validate_mathlib_ci_paths.yml index 29c0e856c66f4c..96731cdcd805ca 100644 --- a/.github/workflows/validate_mathlib_ci_paths.yml +++ b/.github/workflows/validate_mathlib_ci_paths.yml @@ -29,10 +29,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 diff --git a/.github/workflows/weekly-lints.yml b/.github/workflows/weekly-lints.yml index 17615f854293f1..515906d8713e11 100644 --- a/.github/workflows/weekly-lints.yml +++ b/.github/workflows/weekly-lints.yml @@ -26,12 +26,12 @@ jobs: : # Do nothing on failure, but suppress errors fi - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: master - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -66,7 +66,7 @@ jobs: lake build Mathlib.Init - name: Add GitHub problem matcher wrapper - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: add linters: lean @@ -88,13 +88,13 @@ jobs: "${CI_SCRIPTS_DIR}/reporting/zulip_build_report.sh" "${lean_outfile}" > "${GITHUB_OUTPUT}" - name: Remove GitHub problem matcher wrapper - uses: leanprover-community/gh-problem-matcher-wrap@20007cb926a46aa324653a387363b52f07709845 # 2025-04-23 + uses: leanprover-community/gh-problem-matcher-wrap@65a654fcdf7b64ff7633bc7a558f7b46d59a27bf # 2026-06-25 with: action: remove linters: lean - name: Post output to Zulip - uses: zulip/github-actions-zulip/send-message@bd8ec52de371d139ae8313661b7d8318c19266aa # v2.0.1 + uses: zulip/github-actions-zulip/send-message@f675f2b4eb2a95fae974215476dcb7ad8dfeff6b # v2.0.2 with: api-key: ${{ secrets.ZULIP_API_KEY }} email: 'github-mathlib4-bot@leanprover.zulipchat.com' diff --git a/.github/workflows/zulip_emoji_ci_status.yaml b/.github/workflows/zulip_emoji_ci_status.yaml index 02d9ed31903483..8ab30ec4c2ef23 100644 --- a/.github/workflows/zulip_emoji_ci_status.yaml +++ b/.github/workflows/zulip_emoji_ci_status.yaml @@ -74,7 +74,7 @@ jobs: - name: Checkout local actions if: steps.pr.outputs.skip != 'true' && steps.action.outputs.ci_action != 'skip' - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -86,7 +86,7 @@ jobs: - name: Set up Python if: steps.pr.outputs.skip != 'true' && steps.action.outputs.ci_action != 'skip' - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.x' diff --git a/.github/workflows/zulip_emoji_closed_pr.yaml b/.github/workflows/zulip_emoji_closed_pr.yaml index 91d05bc2cdd41e..2dbf54efff0875 100644 --- a/.github/workflows/zulip_emoji_closed_pr.yaml +++ b/.github/workflows/zulip_emoji_closed_pr.yaml @@ -33,7 +33,7 @@ jobs: - name: Checkout local actions if: ${{ ! startsWith(github.event.pull_request.title, '[Merged by Bors]') || github.event_name == 'reopened' }} - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -47,7 +47,7 @@ jobs: - name: Set up Python if: ${{ ! startsWith(github.event.pull_request.title, '[Merged by Bors]') || github.event_name == 'reopened' }} - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.x' diff --git a/.github/workflows/zulip_emoji_labelling.yaml b/.github/workflows/zulip_emoji_labelling.yaml index f92bb24fd007fe..5a019050928fdc 100644 --- a/.github/workflows/zulip_emoji_labelling.yaml +++ b/.github/workflows/zulip_emoji_labelling.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -27,7 +27,7 @@ jobs: uses: ./workflow-actions/.github/actions/get-mathlib-ci - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.x' diff --git a/.github/workflows/zulip_emoji_merge_delegate.yaml b/.github/workflows/zulip_emoji_merge_delegate.yaml index 176bda34b93689..9883c1f0263082 100644 --- a/.github/workflows/zulip_emoji_merge_delegate.yaml +++ b/.github/workflows/zulip_emoji_merge_delegate.yaml @@ -15,12 +15,12 @@ jobs: steps: - name: Checkout mathlib4 repository history - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 # download the full repository - name: Checkout local actions - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: ${{ github.workflow_sha }} fetch-depth: 1 @@ -30,7 +30,7 @@ jobs: uses: ./workflow-actions/.github/actions/get-mathlib-ci - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: '3.x' diff --git a/.gitignore b/.gitignore index ef8c9144af2e08..6973f6af86cc29 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,6 @@ scripts/.rm_set_option_progress.jsonl # Artifacts from using the devcontainer setup .devcontainer/devcontainer-lock.json + +# Output of scripts/export_crossrefs.lean +/crossrefs.json diff --git a/Archive.lean b/Archive.lean index 8ca03e8bc806d9..990ddbc04a894f 100644 --- a/Archive.lean +++ b/Archive.lean @@ -30,6 +30,7 @@ import Archive.Imo.Imo1988Q6 import Archive.Imo.Imo1994Q1 import Archive.Imo.Imo1997Q3 import Archive.Imo.Imo1998Q2 +import Archive.Imo.Imo2000Q2 import Archive.Imo.Imo2001Q2 import Archive.Imo.Imo2001Q3 import Archive.Imo.Imo2001Q4 diff --git a/Archive/Imo/Imo1987Q1.lean b/Archive/Imo/Imo1987Q1.lean index 20c83703c77438..c352c7910d960e 100644 --- a/Archive/Imo/Imo1987Q1.lean +++ b/Archive/Imo/Imo1987Q1.lean @@ -44,9 +44,10 @@ def fixedPointsEquiv : { σx : α × Perm α // σx.2 σx.1 = σx.1 } ≃ Σ x : theorem card_fixed_points : card { σx : α × Perm α // σx.2 σx.1 = σx.1 } = card α * (card α - 1)! := by simp only [card_congr (fixedPointsEquiv α), card_sigma, card_perm] - have (x : _) : ({x}ᶜ : Set α) = Finset.filter (· ≠ x) Finset.univ := by + have h (x : α) : ({x}ᶜ : Set α) = Finset.univ.erase x := by ext; simp - simp [this] + simp_rw [h, Fintype.card_subtype, SetLike.mem_coe, Finset.filter_univ_mem] + simp /-- Given `α : Type*` and `k : ℕ`, `fiber α k` is the set of permutations of `α` with exactly `k` fixed points. -/ diff --git a/Archive/Imo/Imo1998Q2.lean b/Archive/Imo/Imo1998Q2.lean index fe51caef4ee585..2badc27f504822 100644 --- a/Archive/Imo/Imo1998Q2.lean +++ b/Archive/Imo/Imo1998Q2.lean @@ -13,6 +13,7 @@ import Mathlib.Tactic.Ring /-! # IMO 1998 Q2 + In a competition, there are `a` contestants and `b` judges, where `b ≥ 3` is an odd integer. Each judge rates each contestant as either "pass" or "fail". Suppose `k` is a number such that, for any two judges, their ratings coincide for at most `k` contestants. Prove that `k / a ≥ (b - 1) / (2b)`. diff --git a/Archive/Imo/Imo2000Q2.lean b/Archive/Imo/Imo2000Q2.lean new file mode 100644 index 00000000000000..94d7670e8d0faa --- /dev/null +++ b/Archive/Imo/Imo2000Q2.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2026 Aditya Menon. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Aditya Menon +-/ +import Mathlib.Algebra.Order.Archimedean.Real.Basic + +/-! +# IMO 2000 Q2 + +Let `A`, `B`, `C` be positive reals with `ABC = 1`. Prove that +`(A - 1 + 1 / B)(B - 1 + 1 / C)(C - 1 + 1 / A) ≤ 1`. + +## Solution + +We follow the first solution from +. + +We parametrize `A = x / y`, `B = y / z`, `C = z / x` where `x, y, z > 0`. +This reduces the problem to proving `(x - y + z)(y - z + x)(z - x + y) ≤ 8xyz`. + +We then reparametrize `x = q + r`, `y = r + p`, `z = p + q` where `p, q, r ∈ ℝ`, +which transforms the inequality to `8pqr ≤ (q + r)(r + p)(p + q)`. + +The proof splits into cases based on the signs of `p`, `q`, `r`. +When all are positive, AM-GM gives the result. +When at least one is negative or zero, the inequality is verified by sign analysis. + +## Implementation notes + +- The inequality is reduced via `A = x / y`, `B = y / z`, `C = z / x`, then the substitution + `x = q + r`, `y = r + p`, `z = p + q`. +- Helper lemmas prove `8pqr ≤ (p + q)(r + p)(q + r)` by AM-GM when `p, q, r > 0` and by sign + analysis otherwise; the main proof closes with `grind`. + +## References + +* + +-/ + +namespace Imo2000Q2 + +/-- When `p`, `q`, `r > 0`, `8pqr ≤ (p + q)(r + p)(q + r)`, by writing the difference of squares as +a sum of nonnegative squares. -/ +lemma eight_mul_le_prod_add_of_pos {p q r : ℝ} (p_pos : 0 < p) + (q_pos : 0 < q) (r_pos : 0 < r) : + 8 * p * q * r ≤ (p + q) * (q + r) * (r + p) := by + suffices 0 ≤ ((p + q) * (q + r) * (r + p)) ^ 2 - (8 * p * q * r) ^ 2 from + le_of_sq_le_sq (le_of_sub_nonneg this) (by positivity) + calc 0 ≤ (p * (q - r) ^ 2 + q * (r - p) ^ 2 + r * (p - q) ^ 2) + * ((p + q) * (q + r) * (r + p) + 8 * p * q * r) := by positivity + _ = ((p + q) * (q + r) * (r + p)) ^ 2 - (8 * p * q * r) ^ 2 := by ring + +/-- When `p ≤ 0` but `q`, `r > 0` and both pairwise sums are positive, the left side is +nonpositive so the inequality holds. -/ +lemma eight_mul_le_prod_add_of_nonpos {p q r : ℝ} (p_nonpos : p ≤ 0) (r_pos : 0 < r) (q_pos : 0 < q) + (p_add_q_pos : 0 < p + q) (r_add_p_pos : 0 < r + p) : + 8 * p * q * r ≤ (p + q) * (r + p) * (q + r) := by + calc 8 * p * q * r ≤ 0 := by grw [mul_nonpos_of_nonpos_of_nonneg ?_ (by positivity)] + grw [mul_nonpos_of_nonpos_of_nonneg (by grind) (by positivity)] + _ ≤ (p + q) * (r + p) * (q + r) := by positivity + +/-- When all three pairwise sums `p + q`, `r + p`, `q + r` are positive, the inequality holds by +casing on the signs of `p`, `q`, `r`. -/ +lemma eight_mul_le_prod_add_of_add_pos (p q r : ℝ) + (hpq : 0 < p + q := by grind) + (hqr : 0 < q + r := by grind) + (hrp : 0 < r + p := by grind) : + 8 * p * q * r ≤ (p + q) * (q + r) * (r + p) := by + rcases lt_or_ge 0 p with p_pos | p_nonpos <;> + rcases lt_or_ge 0 q with q_pos | q_nonpos <;> + rcases lt_or_ge 0 r with r_pos | r_nonpos + -- At most one of `p`, `q`, `r` can be negative; otherwise some pairwise sum is nonpositive. + · exact eight_mul_le_prod_add_of_pos p_pos q_pos r_pos + · -- `r` is the unique nonpositive variable. + convert eight_mul_le_prod_add_of_nonpos r_nonpos q_pos p_pos hrp hqr using 1 <;> ring + · -- `q` is the unique nonpositive variable. + convert eight_mul_le_prod_add_of_nonpos q_nonpos p_pos r_pos hqr hpq using 1 <;> ring + · linarith + · -- `p` is the unique nonpositive variable. + convert eight_mul_le_prod_add_of_nonpos p_nonpos r_pos q_pos hpq hrp using 1; ring + · linarith + · linarith + · linarith + +/-- **IMO 2000 Q2**. If `A`, `B`, `C > 0` and `ABC = 1`, then +`(A - 1 + 1 / B)(B - 1 + 1 / C)(C - 1 + 1 / A) ≤ 1`. -/ +theorem imo2000_q2 {A B C : ℝ} + (A_pos : 0 < A) (B_pos : 0 < B) (C_pos : 0 < C) (h_prod : A * B * C = 1) : + (A - 1 + 1 / B) * (B - 1 + 1 / C) * (C - 1 + 1 / A) ≤ 1 := by + obtain ⟨x, y, z, x_pos, y_pos, z_pos, rfl, rfl, rfl⟩ : + ∃ x y z, 0 < x ∧ 0 < y ∧ 0 < z ∧ A = x / y ∧ B = y / z ∧ C = z / x := + ⟨A, 1, 1 / B, by grind only [inv_pos]⟩ + -- If `x = q + r`, `y = r + p`, `z = p + q`, it suffices to show `8pqr ≤ (q + r)(r + p)(p + q)`. + have := eight_mul_le_prod_add_of_add_pos ((y + z - x) / 2) ((z + x - y) / 2) ((x + y - z) / 2) + field_simp + grind + +end Imo2000Q2 diff --git a/Archive/Imo/Imo2001Q6.lean b/Archive/Imo/Imo2001Q6.lean index a9398555e019e6..fe5fe2494095ed 100644 --- a/Archive/Imo/Imo2001Q6.lean +++ b/Archive/Imo/Imo2001Q6.lean @@ -9,6 +9,7 @@ import Mathlib.Tactic.LinearCombination /-! # IMO 2001 Q6 + Let $a$, $b$, $c$, $d$ be integers with $a > b > c > d > 0$. Suppose that $$ a*c + b*d = (a + b - c + d) * (-a + b + c + d). $$ diff --git a/Archive/Imo/Imo2005Q3.lean b/Archive/Imo/Imo2005Q3.lean index 5202b5de1bd618..952e5fd6ce5762 100644 --- a/Archive/Imo/Imo2005Q3.lean +++ b/Archive/Imo/Imo2005Q3.lean @@ -11,6 +11,7 @@ import Mathlib.Tactic.Ring /-! # IMO 2005 Q3 + Let `x`, `y` and `z` be positive real numbers such that `xyz ≥ 1`. Prove that: `(x^5 - x^2)/(x^5 + y^2 + z^2) + (y^5 - y^2)/(y^5 + z^2 + x^2) + (z^5 - z^2)/(z^5 + x^2 + y^2) ≥ 0` diff --git a/Archive/Imo/Imo2008Q2.lean b/Archive/Imo/Imo2008Q2.lean index 485185b47bd7bf..695b32fc2160d6 100644 --- a/Archive/Imo/Imo2008Q2.lean +++ b/Archive/Imo/Imo2008Q2.lean @@ -12,6 +12,7 @@ import Mathlib.Tactic.Ring /-! # IMO 2008 Q2 + (a) Prove that ``` x^2 / (x-1)^2 + y^2 / (y-1)^2 + z^2 / (z-1)^2 ≥ 1 diff --git a/Archive/Imo/Imo2008Q3.lean b/Archive/Imo/Imo2008Q3.lean index 8749329ce9d0ce..fcb23b005b7df6 100644 --- a/Archive/Imo/Imo2008Q3.lean +++ b/Archive/Imo/Imo2008Q3.lean @@ -12,6 +12,7 @@ import Mathlib.Tactic.LinearCombination /-! # IMO 2008 Q3 + Prove that there exist infinitely many positive integers `n` such that `n^2 + 1` has a prime divisor which is greater than `2n + √(2n)`. diff --git a/Archive/Imo/Imo2008Q4.lean b/Archive/Imo/Imo2008Q4.lean index 3cb69c48a8537c..69bd134ea246ff 100644 --- a/Archive/Imo/Imo2008Q4.lean +++ b/Archive/Imo/Imo2008Q4.lean @@ -10,6 +10,7 @@ import Mathlib.Tactic.LinearCombination /-! # IMO 2008 Q4 + Find all functions `f : (0,∞) → (0,∞)` (so, `f` is a function from the positive real numbers to the positive real numbers) such that ``` diff --git a/Cache/IO.lean b/Cache/IO.lean index f5e6eac9485adf..c83b5752d383e2 100644 --- a/Cache/IO.lean +++ b/Cache/IO.lean @@ -82,6 +82,10 @@ def CURLBIN := def EXE := if System.Platform.isWindows then ".exe" else "" +/-- The platform's null device, for discarding a command's output: `NUL` on +Windows, `/dev/null` elsewhere. -/ +def nullDevice : String := if System.Platform.isWindows then "NUL" else "/dev/null" + def LAKEPACKAGESDIR : FilePath := ".lake" / "packages" diff --git a/Cache/Infra.lean b/Cache/Infra.lean index 3b133b2d1e3bd7..6379923853ba72 100644 --- a/Cache/Infra.lean +++ b/Cache/Infra.lean @@ -24,6 +24,22 @@ def MATHLIBREPO := "leanprover-community/mathlib4" /-- The full name of the Mathlib nightly-testing GitHub repository. -/ def NIGHTLY_TESTING_REPO := "leanprover-community/mathlib4-nightly-testing" +/-- Whether `repo` is a first-party Mathlib repo rather than a fork. Forks cache +into the per-commit `forks` namespace; the canonical repos do not. -/ +def isCanonicalRepo (repo : String) : Bool := + repo == MATHLIBREPO || repo == NIGHTLY_TESTING_REPO + +/-- +Canonical form of a GitHub `owner/repo` name for use as a cache blob path +segment. + +GitHub treats owner and repository names case-insensitively, while Azure Blob +Storage paths are case-sensitive. Lowercasing yields one shared key whatever +capitalization a remote URL or the GitHub Actions context supplies, so a fork's +uploads and downloads always meet at the same path. +-/ +def normalizeRepo (repo : String) : String := repo.toLower + /-- Trust-classified Azure storage containers for the Mathlib cache. @@ -40,10 +56,11 @@ inductive Container where | nightlyTesting /-- Container for toolchain-PR test runs. -/ | prToolchainTests - /-- The bare `mathlib4` container that older cache clients read from. Only - master CI writes here (mirroring its `mathlib4-master` upload), so those - clients keep finding master-built artifacts; forks and nightly-testing stay - out to keep low-trust writes from reaching readers that predate the split. -/ + /-- The bare `mathlib4` container that older cache clients read from. CI does + not upload here; it is a read-only store of the master-built artifacts that + were mirrored from `mathlib4-master`, kept reachable so those older clients + can resolve them. The `master` container is a self-contained cache, so reads + fall back to `legacy` only for artifacts predating the write cutover. -/ | legacy deriving DecidableEq, Repr, BEq, Inhabited @@ -139,12 +156,9 @@ def defaultContainersForRepo (repo : String) : List Container := if repo == MATHLIBREPO then [.master, .legacy] else if repo == NIGHTLY_TESTING_REPO then - -- Trusted-nightly consumers (`nightly-testing`, `nightly-testing-green`, - -- `bump/*`) read only `nightly-testing` + `legacy`; `pr-toolchain-tests` is - -- excluded so low-trust toolchain-PR uploads can't reach them. Toolchain-PR - -- branches opt into reading their own uploads with `--cache-from=...` (or, - -- in CI, via the `MATHLIB_CACHE_FROM` env var). - [.nightlyTesting, .legacy] + -- `forks` is needed for PRs opened from this repo into mathlib4: their CI + -- uploads land in `forks`. `pr-toolchain-tests` is excluded. + [.nightlyTesting, .forks, .legacy] else -- Forks and everything else: `master` for shared upstream deps, the fork's -- own container for PR-specific files, then `legacy`. diff --git a/Cache/Marker.lean b/Cache/Marker.lean index 56a3e9d43def93..69f830eb0633b3 100644 --- a/Cache/Marker.lean +++ b/Cache/Marker.lean @@ -20,7 +20,9 @@ namespace Cache.Requests open System (FilePath) /-- -URL for the per-SHA marker blob: `{container}/m/{repo}/{sha}`. +URL for the per-SHA marker blob: `{container}/m/{repo}/{sha}`, where `repo` is +lowercased via `normalizeRepo` so the path is case-insensitive in the GitHub +owner/repo name. The marker is uploaded by `put-staged` as the last step when an upload is SHA-scoped (`MATHLIB_CACHE_REPO_SCOPE` set). Its presence at this URL @@ -29,7 +31,7 @@ indicates that the full `.ltar` upload completed for this commit, and lets a blob-listing call. -/ def markerURL (container : Container) (repo sha : String) : String := - s!"{container.azureURL}/m/{repo}/{sha}" + s!"{container.azureURL}/m/{normalizeRepo repo}/{sha}" /-- Upload a tiny marker blob to `/m/{repo}/{sha}` in the given container. The diff --git a/Cache/Query.lean b/Cache/Query.lean index 147f1d16723b4e..2721183acdf9ea 100644 --- a/Cache/Query.lean +++ b/Cache/Query.lean @@ -86,9 +86,11 @@ billed as a Read op. def probeContainerForSHA (container : Container) (repo sha : String) : IO Bool := do let url := markerURL container repo sha + -- Discard the response body to the platform null device (`NUL` on Windows), + -- so curl reports a write error only on a genuine failure, not on every probe. let out ← IO.Process.output {cmd := (← IO.getCurl), - args := #["-s", "-o", "/dev/null", "-w", "%{http_code}", "-I", url], + args := #["-s", "-o", IO.nullDevice, "-w", "%{http_code}", "-I", url], cwd := "."} if out.exitCode != 0 then -- Network error; assume no cache at this SHA @@ -159,9 +161,15 @@ def resolveQueryRepo (repoExplicit? : Option String) : IO String := do Boolean probe for a single commit: prints `cached` or `not cached` and exits with status 0 / 1 respectively. Intended for scripting. -Probes the `forks` container's per-SHA marker, the only SHA-scoped container. +Probes the `forks` per-SHA marker, the only SHA-scoped container. Canonical repos +have no per-commit namespace, so it exits non-zero with a note instead of a +misleading `not cached`. -/ def cacheQuerySingle (repo sha : String) : IO Unit := do + if isCanonicalRepo repo then + IO.eprintln s!"{repo} caches by file hash, not per commit, so there is no per-commit build to query." + (← IO.getStderr).flush + IO.Process.exit 1 let cached ← probeContainerForSHA Container.forks repo sha if cached then IO.println s!"cached: {sha}" @@ -181,6 +189,10 @@ This is a diagnostic-only command: it prints the SHA to stdout but does not auto-apply it. The user manually passes the result to `cache get` if desired. -/ def cacheQuery (repo : String) (cap : Nat := 50) (cwd : FilePath := ".") : IO Unit := do + if isCanonicalRepo repo then + IO.println s!"`cache query` locates a fork PR's per-commit cache. {repo} reads \ + its own cache container directly, so there is nothing to query for it." + return -- Determine merge base with master. If not reachable, use cap-only walk. let mergeBase? ← gitMergeBase "master" cwd let stopRef := mergeBase?.getD "" diff --git a/Cache/Requests.lean b/Cache/Requests.lean index 2b766e6fd7ba47..708ca2515f4c46 100644 --- a/Cache/Requests.lean +++ b/Cache/Requests.lean @@ -348,9 +348,13 @@ container (see `Container.flatPath`), not the repo: the same hash under `container` is `none` for the user-supplied `MATHLIB_CACHE_GET_URL` / `MATHLIB_CACHE_PUT_URL` URLs, where no container policy applies; the path then follows the repo directly — flat for `MATHLIBREPO`, prefixed otherwise. + +`repo` is lowercased via `normalizeRepo` so the repo-namespaced path is +case-insensitive in the GitHub owner/repo name. -/ def mkFileURL (container : Option Container) (repo containerURL fileName : String) (repoScope : Option String := none) : String := + let repo := normalizeRepo repo let flat := match container with | some c => c.flatPath repo | none => repo == MATHLIBREPO @@ -410,24 +414,54 @@ def mkGetConfigContent (container : Option Container) (repo containerURL : Strin pure <| acc ++ s!"url = {mkFileURL container repo containerURL fileName scope?}\n\ -o {(IO.CACHEDIR / (fileName ++ ".part")).toString.quote}\n" +/-- +Whether an HTTP status returned for a single-file read should be treated as a +cache miss (fall through to the next container in the chain) rather than a +transfer failure worth reporting. + +`404` is always a miss. A `403` is a miss only when `treatForbiddenAsMiss` is +set, which callers do for the `legacy` container: when its public read access is +revoked ahead of retirement it answers reads with `403`, and old clients whose +chain still lists `legacy` should fall through quietly instead of printing a +per-file transfer failure. Any other status is a real failure. +-/ +def isCacheMissStatus (httpCode : Nat) (treatForbiddenAsMiss : Bool) : Bool := + httpCode == 404 || (httpCode == 403 && treatForbiddenAsMiss) + +/-- Outcome of a single serial download: the file arrived (`served`), the server +returned a cache miss that should fall through to the next container (`miss`), or +the transfer failed for another reason that should drive the exit code +(`failed`). The parallel path draws the same distinction through +`TransferState.failed` and `isCacheMissStatus`. -/ +inductive DownloadOutcome + | served + | miss + | failed + /-- Calls `curl` to download a single file from a specific container to `CACHEDIR` -(`.cache`). Returns `true` on success, `false` on any error including 404. -`scope?` is the per-round SHA scope (see `mkGetConfigContent`). -/ +(`.cache`). `scope?` is the per-round SHA scope (see `mkGetConfigContent`). +`treatForbiddenAsMiss` mirrors the parallel path: a `legacy` `403` (public read +access revoked ahead of retirement) is a miss, not a failure. -/ def downloadFile (container : Option Container) (repo containerURL : String) - (hash : UInt64) (scope? : Option String) : IO Bool := do + (hash : UInt64) (scope? : Option String) (treatForbiddenAsMiss : Bool := false) : + IO DownloadOutcome := do let fileName := hash.asLTar let url := mkFileURL container repo containerURL fileName scope? let path := IO.CACHEDIR / fileName let partFileName := fileName ++ ".part" let partPath := IO.CACHEDIR / partFileName let out ← IO.Process.output - { cmd := (← IO.getCurl), args := #[url, "--fail", "--silent", "-o", partPath.toString] } + { cmd := (← IO.getCurl), + args := #[url, "--fail", "--silent", "--write-out", "%{http_code}", + "-o", partPath.toString] } if out.exitCode = 0 then IO.FS.rename partPath path - pure true - else - IO.FS.removeFile partPath - pure false + return .served + IO.FS.removeFile partPath + -- `--fail` exits nonzero on any HTTP error; the written-out status tells a 404 + -- miss apart from a real transfer failure (a connection error reports `000`). + let httpCode := out.stdout.trimAscii.toNat?.getD 0 + return if isCacheMissStatus httpCode treatForbiddenAsMiss then .miss else .failed /-- Extract hash from filename (e.g., "/path/to/.cache/00012345.ltar" → 0x12345). Handles both `.ltar` and `.ltar.part` files using `FilePath.fileStem`. -/ @@ -494,20 +528,6 @@ def dispatchDecompBatch (pending : Array (FilePath × Lean.Name)) (config : Deco let task ← IO.asTask (decompressBatch pending config.force config.isMathlibRoot config.mathlibDepPath) return some task -/-- -Whether an HTTP status returned for a single-file read should be treated as a -cache miss (fall through to the next container in the chain) rather than a -transfer failure worth reporting. - -`404` is always a miss. A `403` is a miss only when `treatForbiddenAsMiss` is -set, which callers do for the `legacy` container: when its public read access is -revoked ahead of retirement it answers reads with `403`, and old clients whose -chain still lists `legacy` should fall through quietly instead of printing a -per-file transfer failure. Any other status is a real failure. --/ -def isCacheMissStatus (httpCode : Nat) (treatForbiddenAsMiss : Bool) : Bool := - httpCode == 404 || (httpCode == 403 && treatForbiddenAsMiss) - /-- Whether an HTTP status is the one Azure returns for a blob that already exists, which a non-overwrite `put` (`If-None-Match: *`) hits when it declines to @@ -523,8 +543,11 @@ def monitorCurl (args : Array String) (size : Nat) (caption : String) (speedVar : String) (removeOnError := false) (decompConfig : Option DecompConfig := none) (treatForbiddenAsMiss : Bool := false) - (treatExistsAsSkip : Bool := false) : IO TransferState := do + (treatExistsAsSkip : Bool := false) : IO (TransferState × Std.HashSet UInt64) := do let useAnsi := (← IO.getEnv "TERM").isSome + -- Hashes of the files this pass fetched, used to decide what the next + -- container in the chain still needs to retry. + let servedRef ← IO.mkRef (∅ : Std.HashSet UInt64) let mkStatus (s : TransferState) : String := Id.run do let speedStr := if s.speed != 0 then @@ -557,9 +580,11 @@ def monitorCurl (args : Array String) (size : Nat) if (← System.FilePath.pathExists fn) && fn.endsWith ".part" then let finalPath := (fn.dropEnd 5).copy IO.FS.rename fn finalPath + let hash? := hashFromFileName finalPath + if let some hash := hash? then servedRef.modify (·.insert hash) -- Add to decompression queue if enabled if let some config := decompConfig then - let some hash := hashFromFileName finalPath | do + let some hash := hash? | do IO.eprintln s!"Warning: Failed to extract hash from filename: {finalPath}" decompFailed := decompFailed + 1 let some mod := config.hashToMod[hash]? | do @@ -629,18 +654,19 @@ def monitorCurl (args : Array String) (size : Nat) if s.done > 0 then -- to avoid confusingly moving on without finishing the count IO.eprintln (mkStatus s) - return s + return (s, ← servedRef.get) /-- Run one container's download pass for the given hash map. Returns the -`TransferState` produced by `monitorCurl` (or a synthesized empty state in -serial mode). Side effect: any files successfully fetched are written to -`CACHEDIR` with their final names. -/ +`TransferState` from `monitorCurl` (synthesized in serial mode, where it carries +only the transfer-failure count) and the set of hashes it fetched, so the caller +can carry the rest to the next container. Side effect: any files successfully +fetched are written to `CACHEDIR` with their final names. -/ private def downloadFilesFromContainer (container : Option Container) (repo containerURL : String) (hashMap : IO.ModuleHashMap) (parallel : Bool) (decompConfig : Option DecompConfig) (scope? : Option String) : - IO (Nat × TransferState) := do + IO (TransferState × Std.HashSet UInt64) := do let size := hashMap.size if parallel then IO.FS.writeFile IO.CURLCFG (← mkGetConfigContent container repo containerURL hashMap scope?) @@ -653,16 +679,27 @@ private def downloadFilesFromContainer -- of retirement; treat that as a miss so the chain stays quiet for clients -- whose chain still lists it. let treatForbiddenAsMiss := container == some Container.legacy - let s ← monitorCurl args size "Downloaded" "speed_download" (removeOnError := true) + let (s, served) ← monitorCurl args size "Downloaded" "speed_download" (removeOnError := true) decompConfig (treatForbiddenAsMiss := treatForbiddenAsMiss) IO.FS.removeFile IO.CURLCFG - return (s.failed, s) + return (s, served) else + -- Mirror the parallel path's miss/failure split: a `legacy` 403 is a miss. + let treatForbiddenAsMiss := container == some Container.legacy let r ← hashMap.foldM (init := []) fun acc _ hash => do - pure <| (← IO.asTask do downloadFile container repo containerURL hash scope?) :: acc - let failed := r.foldl (init := 0) fun f t => if let .ok true := t.get then f else f + 1 - let emptyState : TransferState := ⟨0, 0, 0, 0, 0, #[], none, 0, 0, 0⟩ - return (failed, emptyState) + pure <| (hash, ← IO.asTask do + downloadFile container repo containerURL hash scope? treatForbiddenAsMiss) :: acc + -- Served hashes carry the remaining files to the next container; hard + -- failures (anything but a 404/legacy-403 miss, including a task that threw) + -- feed `TransferState.failed`, so they drive the exit code exactly as the + -- parallel path threads its own `failed` count. + let (served, failed) := r.foldl (init := ((∅ : Std.HashSet UInt64), 0)) + fun (served, failed) (hash, t) => + match t.get with + | .ok .served => (served.insert hash, failed) + | .ok .miss => (served, failed) + | _ => (served, failed + 1) + return (⟨0, 0, failed, 0, 0, #[], none, 0, 0, 0⟩, served) /-- Expand the trust-ordered container list into the concrete download rounds to run, each carrying the SHA scope to read at. A round is @@ -745,6 +782,8 @@ def downloadFiles let unsafeMode := !unsafeScopes.isEmpty let mut remaining := hashMap let mut finalState : TransferState := ⟨0, 0, 0, 0, 0, #[], none, 0, 0, 0⟩ + -- Hard transfer failures (not 404 misses) drive the exit code; misses are + -- normal and instead surface as the "not found" hint keyed on `remaining`. let mut downloadFailed := 0 -- For the `--unsafe` summary: how many files each scoped (forks) round supplied, -- attributed by the drop in `remaining` across that round. @@ -754,13 +793,14 @@ def downloadFiles let scopeNote := match roundScope? with | some s => s!" (scope {s})" | none => "" IO.println s!"Attempting to download {remaining.size} file(s) from {repo} cache at {url}{scopeNote}" let before := remaining.size - let (failed, s) ← downloadFilesFromContainer container? repo url remaining parallel decompConfig roundScope? - -- Carry forward the decompression-related state across container rounds. - -- Counter fields (success/failed/done) reflect only the last round; we - -- aggregate `downloadFailed` separately below. + let (s, served) ← downloadFilesFromContainer container? repo url remaining parallel decompConfig roundScope? + -- Keep the latest round's pipeline state and transfer-failure count for the + -- finalization and exit-code logic below. Drop the files this round served so + -- the next container only retries genuine misses, regardless of what is + -- already on disk. finalState := s - downloadFailed := failed - remaining ← remaining.filterExists false + downloadFailed := s.failed + remaining := remaining.filter fun _ hash => !served.contains hash if unsafeMode then if let some sha := roundScope? then scopeServed := scopeServed.push (sha, before - remaining.size) @@ -778,7 +818,7 @@ def downloadFiles if remaining.size > 0 then IO.eprintln s!" {remaining.size} file(s) still missing after all scopes." - if warnOnMissing && downloadFailed > 0 && parallel then + if warnOnMissing && !remaining.isEmpty then IO.eprintln "Warning: some files were not found in the cache." IO.eprintln "This usually means that your local checkout of mathlib4 has diverged from upstream." IO.eprintln "" @@ -1045,7 +1085,7 @@ def putFilesAbsolute "-X", "PUT", "--parallel", "--retry", "5", -- there seem to be some intermittent failures "--write-out", "%{json}\n", "--config", tempConfigFilePath.toString] - let s ← monitorCurl args size "Uploaded" "speed_upload" (removeOnError := false) + let (s, _) ← monitorCurl args size "Uploaded" "speed_upload" (removeOnError := false) (decompConfig := none) (treatExistsAsSkip := !overwrite) IO.FS.removeFile tempConfigFilePath -- Surface genuine upload failures. Already-present blobs (409/412 on a diff --git a/Cache/Test.lean b/Cache/Test.lean index 15bf2c1b701839..e9aa2298bdefd9 100644 --- a/Cache/Test.lean +++ b/Cache/Test.lean @@ -74,14 +74,14 @@ def assertEq (name expected actual : String) : IO Unit := do IO.eprintln s!" FAIL: {name}\n expected: {expected}\n actual: {actual}" failures.modify (· + 1) -/-- Run `action` with both stdout and stderr redirected to /dev/null. Restores -both on completion, including on exception. Apply this to every production code -call in tests so diagnostic prints never mix with test output, regardless of -whether the production code currently produces any. -/ +/-- Run `action` with both stdout and stderr redirected to the platform null +device. Restores both on completion, including on exception. Apply this to every +production code call in tests so diagnostic prints never mix with test output, +regardless of whether the production code currently produces any. -/ private def withSuppressedOutput (action : IO α) : IO α := do let savedOut ← IO.getStdout let savedErr ← IO.getStderr - let sink ← IO.FS.Handle.mk "/dev/null" IO.FS.Mode.append + let sink ← IO.FS.Handle.mk Cache.IO.nullDevice IO.FS.Mode.append let sinkStream := IO.FS.Stream.ofHandle sink -- `IO.setStdout`/`IO.setStderr` return the previous stream; we already saved it, -- so discard the return value here. @@ -200,8 +200,8 @@ def test_defaultContainersForRepo : IO Unit := do IO.println "defaultContainersForRepo:" assert "canonical repo → [master, legacy]" (defaultContainersForRepo MATHLIBREPO == [.master, .legacy]) - assert "nightly-testing repo → [nightly-testing, legacy], no pr-toolchain-tests" - (defaultContainersForRepo NIGHTLY_TESTING_REPO == [.nightlyTesting, .legacy]) + assert "nightly-testing repo → [nightly-testing, forks, legacy], no pr-toolchain-tests" + (defaultContainersForRepo NIGHTLY_TESTING_REPO == [.nightlyTesting, .forks, .legacy]) assert "fork repo → [master, forks, legacy]" (defaultContainersForRepo "alice/mathlib4" == [.master, .forks, .legacy]) assert "unknown repo falls back to the fork chain" @@ -279,6 +279,11 @@ def test_mkFileURL : IO Unit := do assertEq "scope is ignored on a flat legacy path" "https://lakecache.blob.core.windows.net/mathlib4/f/abc.ltar" (mkFileURL (some .legacy) MATHLIBREPO Container.legacy.azureURL "abc.ltar" (some "abc123def")) + -- The repo segment is lowercased, so a mixed-case GitHub owner resolves to the + -- same path whether it reaches the cache from CI or a local remote URL. + assertEq "fork repo is lowercased in the path" + "https://lakecache.blob.core.windows.net/mathlib4-forks/f/alice/mathlib4/abc.ltar" + (mkFileURL (some .forks) "Alice/Mathlib4" Container.forks.azureURL "abc.ltar") end MkFileURL @@ -476,6 +481,11 @@ def test_markerURL : IO Unit := do assertEq "marker URL respects the container base" "https://lakecache.blob.core.windows.net/mathlib4/m/someorg/mathlib4/sha9999" (markerURL .legacy "someorg/mathlib4" "sha9999") + -- The repo segment is lowercased, so an upload and a probe for the same fork + -- meet at one path regardless of how the owner name was capitalized. + assertEq "marker repo is lowercased in the path" + "https://lakecache.blob.core.windows.net/mathlib4-forks/m/alice/mathlib4/abc123" + (markerURL .forks "Alice/Mathlib4" "abc123") end Marker diff --git a/Cache/Warning.lean b/Cache/Warning.lean index 2aba0a12428b87..c878c90028c800 100644 --- a/Cache/Warning.lean +++ b/Cache/Warning.lean @@ -179,8 +179,8 @@ Fires only on naive `cache get` invocations: picked a scope and the non-default-scope warning is doing the talking) - no `--cache-from` override (else they've already taken explicit responsibility for the lookup chain) -- the resolved repo's default lookup chain reads from `forks` (otherwise SHA - scoping is not relevant) +- the repo is a fork, not a first-party repo: the canonical repos don't build + into the per-commit `forks` namespace this note checks - HEAD is not already an ancestor of `master`. From a personal-fork checkout sitting on `master` (or an undiverged branch), the fork's SHA-scoped marker is structurally absent, but `master` is first in the fork lookup chain and serves @@ -195,7 +195,7 @@ mix with `cache get`'s stdout output. def informIfHeadNotBuilt (repo : String) : IO Unit := do if (← getRepoScope).isSome then return if (← cacheFromOverride.get).isSome then return - unless (defaultContainersForRepo repo).contains Container.forks do return + if isCanonicalRepo repo then return -- HEAD already on (an ancestor of) master: master CI builds these commits and -- the master container (first in the fork lookup chain) serves their artifacts -- by hash, so there is nothing fork-specific to build. The forks marker is diff --git a/Counterexamples/DirectSumIsInternal.lean b/Counterexamples/DirectSumIsInternal.lean index c46125e7464b48..b2f95cb70a44d2 100644 --- a/Counterexamples/DirectSumIsInternal.lean +++ b/Counterexamples/DirectSumIsInternal.lean @@ -56,8 +56,10 @@ theorem withSign.isCompl : IsCompl ℤ≥0 ℤ≤0 := by · exact Submodule.mem_sup_left (mem_withSign_one.mpr hp) · exact Submodule.mem_sup_right (mem_withSign_neg_one.mpr hn) -lemma withSign.independent : iSupIndep withSign := by - apply (iSupIndep_pair UnitsInt.one_ne_neg_one _).mpr withSign.isCompl.disjoint +set_option linter.defProp false in +def withSign.independent : iSupIndep withSign := by + apply + (iSupIndep_pair UnitsInt.one_ne_neg_one _).mpr withSign.isCompl.disjoint intro i fin_cases i <;> simp diff --git a/Counterexamples/DiscreteTopologyNonDiscreteUniformity.lean b/Counterexamples/DiscreteTopologyNonDiscreteUniformity.lean index c671fb9407ba99..bd6548b6cf585d 100644 --- a/Counterexamples/DiscreteTopologyNonDiscreteUniformity.lean +++ b/Counterexamples/DiscreteTopologyNonDiscreteUniformity.lean @@ -7,6 +7,7 @@ import Mathlib.Analysis.SpecificLimits.Basic /-! # Discrete uniformities and discrete topology + Exactly as different metrics can induce equivalent topologies on a space, it is possible that different uniform structures (a notion that generalises that of a metric structure) induce the same topology on a space. In this file we are concerned in particular with the *discrete topology*, diff --git a/Counterexamples/NowhereDifferentiable.lean b/Counterexamples/NowhereDifferentiable.lean index 998caa01d49e12..2e1abcba12e4e7 100644 --- a/Counterexamples/NowhereDifferentiable.lean +++ b/Counterexamples/NowhereDifferentiable.lean @@ -76,8 +76,8 @@ theorem uniformContinuous_weierstrass {a : ℝ} (ha : a ∈ Set.Ioo 0 1) (b : To show that Weierstrass function $f(x)$ is not differentiable at any $x$, we choose a sequence $\{x_m\}$ such that, as $m\to\infty$ - - $\{x_m\}$ converges to $x$ - - The slope $(f(x_m) - f(x)) / (x_m - x)$ grows unbounded, +- $\{x_m\}$ converges to $x$ +- The slope $(f(x_m) - f(x)) / (x_m - x)$ grows unbounded, which means the derivative $f'(x)$ cannot exist. -/ diff --git a/Counterexamples/PeanoCurve.lean b/Counterexamples/PeanoCurve.lean index c919bfda630107..cf1843125341fa 100644 --- a/Counterexamples/PeanoCurve.lean +++ b/Counterexamples/PeanoCurve.lean @@ -8,6 +8,7 @@ import Mathlib.Topology.MetricSpace.HausdorffAlexandroff /-! # Peano curve + This file proves the existence of a Peano curve -- continuous surjective map from the interval `[0, 1]` onto the square `[0, 1] × [0, 1]`. -/ diff --git a/Counterexamples/Phillips.lean b/Counterexamples/Phillips.lean index 1fc73e0eda9341..384d1931679214 100644 --- a/Counterexamples/Phillips.lean +++ b/Counterexamples/Phillips.lean @@ -432,7 +432,7 @@ theorem toFunctions_toMeasure [MeasurableSpace α] (μ : Measure α) [IsFiniteMe set_option backward.isDefEq.respectTransparency false in theorem toFunctions_toMeasure_continuousPart [MeasurableSpace α] [MeasurableSingletonClass α] - (μ : Measure α) [IsFiniteMeasure μ] [NoAtoms μ] (s : Set α) (hs : MeasurableSet s) : + (μ : Measure α) [IsFiniteMeasure μ] [NullSingletonClass μ] (s : Set α) (hs : MeasurableSet s) : μ.extensionToBoundedFunctions.toBoundedAdditiveMeasure.continuousPart s = μ.real s := by let f := μ.extensionToBoundedFunctions.toBoundedAdditiveMeasure change f (univ \ f.discreteSupport ∩ s) = μ.real s diff --git a/Counterexamples/Pseudoelement.lean b/Counterexamples/Pseudoelement.lean index f6307491cc1b0e..fa640e6f0af98e 100644 --- a/Counterexamples/Pseudoelement.lean +++ b/Counterexamples/Pseudoelement.lean @@ -8,6 +8,7 @@ import Mathlib.Algebra.Category.ModuleCat.Biproducts /-! # Pseudoelements and pullbacks + Borceux claims in Proposition 1.9.5 that the pseudoelement constructed in `CategoryTheory.Abelian.Pseudoelement.pseudo_pullback` is unique. We show here that this claim is false. This means in particular that we cannot have an extensionality principle for pullbacks in diff --git a/Mathlib.lean b/Mathlib.lean index 8325b39eb23a67..e5a34fe708f1ce 100644 --- a/Mathlib.lean +++ b/Mathlib.lean @@ -100,6 +100,7 @@ public import Mathlib.Algebra.Category.AlgCat.FilteredColimits public import Mathlib.Algebra.Category.AlgCat.Limits public import Mathlib.Algebra.Category.AlgCat.Monoidal public import Mathlib.Algebra.Category.AlgCat.Symmetric +public import Mathlib.Algebra.Category.AlgCat.TensorAlgebra public import Mathlib.Algebra.Category.BialgCat.Basic public import Mathlib.Algebra.Category.BialgCat.Monoidal public import Mathlib.Algebra.Category.BoolRing @@ -111,7 +112,6 @@ public import Mathlib.Algebra.Category.CommAlgCat.FiniteType public import Mathlib.Algebra.Category.CommAlgCat.Monoidal public import Mathlib.Algebra.Category.CommBialgCat public import Mathlib.Algebra.Category.CommHopfAlgCat -public import Mathlib.Algebra.Category.ContinuousCohomology.Basic public import Mathlib.Algebra.Category.FGModuleCat.Abelian public import Mathlib.Algebra.Category.FGModuleCat.Basic public import Mathlib.Algebra.Category.FGModuleCat.Colimits @@ -193,6 +193,7 @@ public import Mathlib.Algebra.Category.ModuleCat.Presheaf.Pushforward public import Mathlib.Algebra.Category.ModuleCat.Presheaf.PushforwardZeroMonoidal public import Mathlib.Algebra.Category.ModuleCat.Presheaf.Sheafification public import Mathlib.Algebra.Category.ModuleCat.Presheaf.Sheafify +public import Mathlib.Algebra.Category.ModuleCat.Presheaf.Submodule public import Mathlib.Algebra.Category.ModuleCat.Products public import Mathlib.Algebra.Category.ModuleCat.Projective public import Mathlib.Algebra.Category.ModuleCat.ProjectiveDimension @@ -573,6 +574,7 @@ public import Mathlib.Algebra.Homology.ComplexShape public import Mathlib.Algebra.Homology.ComplexShapeSigns public import Mathlib.Algebra.Homology.ConcreteCategory public import Mathlib.Algebra.Homology.DerivedCategory.Basic +public import Mathlib.Algebra.Homology.DerivedCategory.DerivabilityStructureInjectives public import Mathlib.Algebra.Homology.DerivedCategory.ExactFunctor public import Mathlib.Algebra.Homology.DerivedCategory.Ext.Basic public import Mathlib.Algebra.Homology.DerivedCategory.Ext.EnoughInjectives @@ -589,6 +591,8 @@ public import Mathlib.Algebra.Homology.DerivedCategory.HomologySequence public import Mathlib.Algebra.Homology.DerivedCategory.KInjective public import Mathlib.Algebra.Homology.DerivedCategory.KProjective public import Mathlib.Algebra.Homology.DerivedCategory.Linear +public import Mathlib.Algebra.Homology.DerivedCategory.Plus +public import Mathlib.Algebra.Homology.DerivedCategory.RightDerivedFunctorPlus public import Mathlib.Algebra.Homology.DerivedCategory.ShortExact public import Mathlib.Algebra.Homology.DerivedCategory.SingleTriangle public import Mathlib.Algebra.Homology.DerivedCategory.SmallShiftedHom @@ -618,6 +622,7 @@ public import Mathlib.Algebra.Homology.ExactSequenceFour public import Mathlib.Algebra.Homology.Factorizations.Basic public import Mathlib.Algebra.Homology.Factorizations.CM5a public import Mathlib.Algebra.Homology.Factorizations.CM5b +public import Mathlib.Algebra.Homology.FullSubcategory public import Mathlib.Algebra.Homology.Functor public import Mathlib.Algebra.Homology.GrothendieckAbelian public import Mathlib.Algebra.Homology.HasNoLoop @@ -715,7 +720,6 @@ public import Mathlib.Algebra.Lie.Basic public import Mathlib.Algebra.Lie.Basis public import Mathlib.Algebra.Lie.CartanCriterion public import Mathlib.Algebra.Lie.CartanExists -public import Mathlib.Algebra.Lie.CartanMatrix public import Mathlib.Algebra.Lie.CartanSubalgebra public import Mathlib.Algebra.Lie.Character public import Mathlib.Algebra.Lie.Classical @@ -764,6 +768,7 @@ public import Mathlib.Algebra.Lie.Weights.Killing public import Mathlib.Algebra.Lie.Weights.Linear public import Mathlib.Algebra.Lie.Weights.RootSystem public import Mathlib.Algebra.LieRinehartAlgebra.Defs +public import Mathlib.Algebra.LieRinehartAlgebra.Subalgebra public import Mathlib.Algebra.LinearRecurrence public import Mathlib.Algebra.ModEq public import Mathlib.Algebra.Module.Basic @@ -920,6 +925,7 @@ public import Mathlib.Algebra.Order.Antidiag.FinsuppEquiv public import Mathlib.Algebra.Order.Antidiag.Nat public import Mathlib.Algebra.Order.Antidiag.Pi public import Mathlib.Algebra.Order.Antidiag.Prod +public import Mathlib.Algebra.Order.Antidiag.Tendsto public import Mathlib.Algebra.Order.Archimedean.Basic public import Mathlib.Algebra.Order.Archimedean.Class public import Mathlib.Algebra.Order.Archimedean.Defs @@ -1030,7 +1036,6 @@ public import Mathlib.Algebra.Order.Interval.Set.SuccPred public import Mathlib.Algebra.Order.Invertible public import Mathlib.Algebra.Order.IsBotOne public import Mathlib.Algebra.Order.Kleene -public import Mathlib.Algebra.Order.Module.Algebra public import Mathlib.Algebra.Order.Module.Archimedean public import Mathlib.Algebra.Order.Module.Basic public import Mathlib.Algebra.Order.Module.Defs @@ -1165,7 +1170,6 @@ public import Mathlib.Algebra.Polynomial.Eval.Irreducible public import Mathlib.Algebra.Polynomial.Eval.SMul public import Mathlib.Algebra.Polynomial.Eval.Subring public import Mathlib.Algebra.Polynomial.Expand -public import Mathlib.Algebra.Polynomial.Factors public import Mathlib.Algebra.Polynomial.FieldDivision public import Mathlib.Algebra.Polynomial.GroupRingAction public import Mathlib.Algebra.Polynomial.HasseDeriv @@ -1343,7 +1347,9 @@ public import Mathlib.AlgebraicGeometry.AffineScheme public import Mathlib.AlgebraicGeometry.AffineSpace public import Mathlib.AlgebraicGeometry.AffineTransitionLimit public import Mathlib.AlgebraicGeometry.AlgClosed.Basic +public import Mathlib.AlgebraicGeometry.AlgebraicCycle.Basic public import Mathlib.AlgebraicGeometry.Artinian +public import Mathlib.AlgebraicGeometry.Birational.Birational public import Mathlib.AlgebraicGeometry.Birational.Dominant public import Mathlib.AlgebraicGeometry.Birational.RationalMap public import Mathlib.AlgebraicGeometry.ColimitsOver @@ -1435,6 +1441,7 @@ public import Mathlib.AlgebraicGeometry.Morphisms.WeaklyEtale public import Mathlib.AlgebraicGeometry.Noetherian public import Mathlib.AlgebraicGeometry.Normalization public import Mathlib.AlgebraicGeometry.OpenImmersion +public import Mathlib.AlgebraicGeometry.OrderOfVanishing public import Mathlib.AlgebraicGeometry.Over public import Mathlib.AlgebraicGeometry.PointsPi public import Mathlib.AlgebraicGeometry.ProjectiveSpectrum.Basic @@ -1531,6 +1538,7 @@ public import Mathlib.AlgebraicTopology.Quasicategory.Nerve public import Mathlib.AlgebraicTopology.Quasicategory.StrictBicategory public import Mathlib.AlgebraicTopology.Quasicategory.StrictSegal public import Mathlib.AlgebraicTopology.Quasicategory.TwoTruncated +public import Mathlib.AlgebraicTopology.Reedy.Basic public import Mathlib.AlgebraicTopology.RelativeCellComplex.AttachCells public import Mathlib.AlgebraicTopology.RelativeCellComplex.Basic public import Mathlib.AlgebraicTopology.SimplexCategory.Augmented.Basic @@ -1602,6 +1610,7 @@ public import Mathlib.AlgebraicTopology.SimplicialSet.NonDegenerateSimplicesColi public import Mathlib.AlgebraicTopology.SimplicialSet.NonDegenerateSimplicesSubcomplex public import Mathlib.AlgebraicTopology.SimplicialSet.Nonempty public import Mathlib.AlgebraicTopology.SimplicialSet.Nonsingular +public import Mathlib.AlgebraicTopology.SimplicialSet.NonsingularColimit public import Mathlib.AlgebraicTopology.SimplicialSet.Op public import Mathlib.AlgebraicTopology.SimplicialSet.Path public import Mathlib.AlgebraicTopology.SimplicialSet.PiZero @@ -1843,6 +1852,7 @@ public import Mathlib.Analysis.Calculus.TangentCone.ProperSpace public import Mathlib.Analysis.Calculus.TangentCone.Real public import Mathlib.Analysis.Calculus.TangentCone.Seq public import Mathlib.Analysis.Calculus.Taylor +public import Mathlib.Analysis.Calculus.TaylorIntegral public import Mathlib.Analysis.Calculus.UniformLimitsDeriv public import Mathlib.Analysis.Calculus.VectorField public import Mathlib.Analysis.Complex.AbelLimit @@ -2173,6 +2183,7 @@ public import Mathlib.Analysis.Normed.Group.Tannery public import Mathlib.Analysis.Normed.Group.Ultra public import Mathlib.Analysis.Normed.Group.Uniform public import Mathlib.Analysis.Normed.Group.ZeroAtInfty +public import Mathlib.Analysis.Normed.Lp.Finsupp public import Mathlib.Analysis.Normed.Lp.LpEquiv public import Mathlib.Analysis.Normed.Lp.Matrix public import Mathlib.Analysis.Normed.Lp.MeasurableSpace @@ -2198,7 +2209,6 @@ public import Mathlib.Analysis.Normed.Module.ContinuousInverse public import Mathlib.Analysis.Normed.Module.Convex public import Mathlib.Analysis.Normed.Module.DoubleDual public import Mathlib.Analysis.Normed.Module.Dual -public import Mathlib.Analysis.Normed.Module.ENormedSpace public import Mathlib.Analysis.Normed.Module.Extr public import Mathlib.Analysis.Normed.Module.FiniteDimension public import Mathlib.Analysis.Normed.Module.HahnBanach @@ -2392,7 +2402,6 @@ public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Arctan public import Mathlib.Analysis.SpecialFunctions.Trigonometric.ArctanDeriv public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Basic public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Bounds -public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.Basic public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.ChebyshevGauss public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.Extremal @@ -2869,6 +2878,7 @@ public import Mathlib.CategoryTheory.Limits.Preserves.Filtered public import Mathlib.CategoryTheory.Limits.Preserves.Finite public import Mathlib.CategoryTheory.Limits.Preserves.FunctorCategory public import Mathlib.CategoryTheory.Limits.Preserves.Grothendieck +public import Mathlib.CategoryTheory.Limits.Preserves.Lattice public import Mathlib.CategoryTheory.Limits.Preserves.Limits public import Mathlib.CategoryTheory.Limits.Preserves.Opposites public import Mathlib.CategoryTheory.Limits.Preserves.Over @@ -2985,6 +2995,11 @@ public import Mathlib.CategoryTheory.Limits.Types.Pushouts public import Mathlib.CategoryTheory.Limits.Types.Yoneda public import Mathlib.CategoryTheory.Limits.Unit public import Mathlib.CategoryTheory.Limits.VanKampen +public import Mathlib.CategoryTheory.Limits.WeakLimits.Basic +public import Mathlib.CategoryTheory.Limits.WeakLimits.WeakEqualizers +public import Mathlib.CategoryTheory.Limits.WeakLimits.WeakKernels +public import Mathlib.CategoryTheory.Limits.WeakLimits.WeakPullbacks +public import Mathlib.CategoryTheory.Limits.Weighted.HasWeightedLimit public import Mathlib.CategoryTheory.Limits.Yoneda public import Mathlib.CategoryTheory.Linear.Basic public import Mathlib.CategoryTheory.Linear.FunctorCategory @@ -3172,6 +3187,7 @@ public import Mathlib.CategoryTheory.MorphismProperty.IsSmall public import Mathlib.CategoryTheory.MorphismProperty.LiftingProperty public import Mathlib.CategoryTheory.MorphismProperty.Limits public import Mathlib.CategoryTheory.MorphismProperty.Local +public import Mathlib.CategoryTheory.MorphismProperty.LocalClosure public import Mathlib.CategoryTheory.MorphismProperty.LocalEpi public import Mathlib.CategoryTheory.MorphismProperty.OfObjectProperty public import Mathlib.CategoryTheory.MorphismProperty.OverAdjunction @@ -3190,6 +3206,7 @@ public import Mathlib.CategoryTheory.ObjectProperty.ClosureShift public import Mathlib.CategoryTheory.ObjectProperty.ColimitsCardinalClosure public import Mathlib.CategoryTheory.ObjectProperty.ColimitsClosure public import Mathlib.CategoryTheory.ObjectProperty.ColimitsOfShape +public import Mathlib.CategoryTheory.ObjectProperty.Comma public import Mathlib.CategoryTheory.ObjectProperty.CompleteLattice public import Mathlib.CategoryTheory.ObjectProperty.ContainsZero public import Mathlib.CategoryTheory.ObjectProperty.EpiMono @@ -3223,12 +3240,16 @@ public import Mathlib.CategoryTheory.Preadditive.AdditiveFunctor public import Mathlib.CategoryTheory.Preadditive.Basic public import Mathlib.CategoryTheory.Preadditive.Biproducts public import Mathlib.CategoryTheory.Preadditive.CommGrp_ +public import Mathlib.CategoryTheory.Preadditive.Comma public import Mathlib.CategoryTheory.Preadditive.EilenbergMoore public import Mathlib.CategoryTheory.Preadditive.EndoFunctor +public import Mathlib.CategoryTheory.Preadditive.FreydCategory.Homotopy +public import Mathlib.CategoryTheory.Preadditive.FreydCategory.RightFreyd public import Mathlib.CategoryTheory.Preadditive.FunctorCategory public import Mathlib.CategoryTheory.Preadditive.HomOrthogonal public import Mathlib.CategoryTheory.Preadditive.Indization public import Mathlib.CategoryTheory.Preadditive.Injective.Basic +public import Mathlib.CategoryTheory.Preadditive.Injective.InjectiveObject public import Mathlib.CategoryTheory.Preadditive.Injective.LiftingProperties public import Mathlib.CategoryTheory.Preadditive.Injective.Preserves public import Mathlib.CategoryTheory.Preadditive.Injective.Resolution @@ -3264,6 +3285,7 @@ public import Mathlib.CategoryTheory.Presentable.LocallyPresentable public import Mathlib.CategoryTheory.Presentable.OrthogonalReflection public import Mathlib.CategoryTheory.Presentable.Presheaf public import Mathlib.CategoryTheory.Presentable.Retracts +public import Mathlib.CategoryTheory.Presentable.SharplyLT.Basic public import Mathlib.CategoryTheory.Presentable.StrongGenerator public import Mathlib.CategoryTheory.Presentable.Type public import Mathlib.CategoryTheory.Products.Associator @@ -3363,6 +3385,7 @@ public import Mathlib.CategoryTheory.Sites.JointlySurjective public import Mathlib.CategoryTheory.Sites.LeftExact public import Mathlib.CategoryTheory.Sites.Limits public import Mathlib.CategoryTheory.Sites.LocalProperties +public import Mathlib.CategoryTheory.Sites.LocalSite public import Mathlib.CategoryTheory.Sites.Localization public import Mathlib.CategoryTheory.Sites.LocallyBijective public import Mathlib.CategoryTheory.Sites.LocallyFullyFaithful @@ -3445,13 +3468,6 @@ public import Mathlib.CategoryTheory.Subobject.NoetherianObject public import Mathlib.CategoryTheory.Subobject.Presheaf public import Mathlib.CategoryTheory.Subobject.Types public import Mathlib.CategoryTheory.Subobject.WellPowered -public import Mathlib.CategoryTheory.Subpresheaf.Basic -public import Mathlib.CategoryTheory.Subpresheaf.Equalizer -public import Mathlib.CategoryTheory.Subpresheaf.Finite -public import Mathlib.CategoryTheory.Subpresheaf.Image -public import Mathlib.CategoryTheory.Subpresheaf.OfSection -public import Mathlib.CategoryTheory.Subpresheaf.Sieves -public import Mathlib.CategoryTheory.Subpresheaf.Subobject public import Mathlib.CategoryTheory.Subterminal public import Mathlib.CategoryTheory.Sums.Associator public import Mathlib.CategoryTheory.Sums.Basic @@ -3487,6 +3503,7 @@ public import Mathlib.CategoryTheory.Triangulated.TStructure.TruncLEGT public import Mathlib.CategoryTheory.Triangulated.TStructure.TruncLTGE public import Mathlib.CategoryTheory.Triangulated.TriangleShift public import Mathlib.CategoryTheory.Triangulated.Triangulated +public import Mathlib.CategoryTheory.Triangulated.WeakKernels public import Mathlib.CategoryTheory.Triangulated.Yoneda public import Mathlib.CategoryTheory.Types.Basic public import Mathlib.CategoryTheory.Types.Epimorphisms @@ -3625,6 +3642,7 @@ public import Mathlib.Combinatorics.SimpleGraph.Connectivity.Subgraph public import Mathlib.Combinatorics.SimpleGraph.Connectivity.WalkCounting public import Mathlib.Combinatorics.SimpleGraph.Connectivity.WalkDecomp public import Mathlib.Combinatorics.SimpleGraph.Copy +public import Mathlib.Combinatorics.SimpleGraph.CycleGraph public import Mathlib.Combinatorics.SimpleGraph.Dart public import Mathlib.Combinatorics.SimpleGraph.DegreeSum public import Mathlib.Combinatorics.SimpleGraph.DeleteEdges @@ -3634,6 +3652,7 @@ public import Mathlib.Combinatorics.SimpleGraph.EdgeLabeling public import Mathlib.Combinatorics.SimpleGraph.Ends.Defs public import Mathlib.Combinatorics.SimpleGraph.Ends.Properties public import Mathlib.Combinatorics.SimpleGraph.Extremal.Basic +public import Mathlib.Combinatorics.SimpleGraph.Extremal.ErdosStoneSimonovits public import Mathlib.Combinatorics.SimpleGraph.Extremal.Turan public import Mathlib.Combinatorics.SimpleGraph.Extremal.TuranDensity public import Mathlib.Combinatorics.SimpleGraph.Finite @@ -4026,6 +4045,7 @@ public import Mathlib.Data.Int.Sqrt public import Mathlib.Data.Int.Star public import Mathlib.Data.Int.SuccPred public import Mathlib.Data.Int.WithZero +public import Mathlib.Data.LawfulXor public import Mathlib.Data.List.AList public import Mathlib.Data.List.Basic public import Mathlib.Data.List.Chain @@ -4047,7 +4067,6 @@ public import Mathlib.Data.List.Indexes public import Mathlib.Data.List.Induction public import Mathlib.Data.List.Infix public import Mathlib.Data.List.InsertIdx -public import Mathlib.Data.List.InsertNth public import Mathlib.Data.List.Intervals public import Mathlib.Data.List.Iterate public import Mathlib.Data.List.Lattice @@ -4170,7 +4189,6 @@ public import Mathlib.Data.Nat.Choose.Central public import Mathlib.Data.Nat.Choose.Dvd public import Mathlib.Data.Nat.Choose.Factorization public import Mathlib.Data.Nat.Choose.Lucas -public import Mathlib.Data.Nat.Choose.Mul public import Mathlib.Data.Nat.Choose.Multinomial public import Mathlib.Data.Nat.Choose.Sum public import Mathlib.Data.Nat.Choose.Vandermonde @@ -4179,6 +4197,7 @@ public import Mathlib.Data.Nat.Digits.Defs public import Mathlib.Data.Nat.Digits.Div public import Mathlib.Data.Nat.Digits.Lemmas public import Mathlib.Data.Nat.Dist +public import Mathlib.Data.Nat.DvdSequence public import Mathlib.Data.Nat.EvenOddRec public import Mathlib.Data.Nat.Factorial.Basic public import Mathlib.Data.Nat.Factorial.BigOperators @@ -4707,6 +4726,7 @@ public import Mathlib.GroupTheory.Congruence.BigOperators public import Mathlib.GroupTheory.Congruence.Defs public import Mathlib.GroupTheory.Congruence.Hom public import Mathlib.GroupTheory.Congruence.Opposite +public import Mathlib.GroupTheory.Congruence.Star public import Mathlib.GroupTheory.Coprod.Basic public import Mathlib.GroupTheory.CoprodI public import Mathlib.GroupTheory.Coset.Basic @@ -5078,6 +5098,7 @@ public import Mathlib.LinearAlgebra.Matrix.Circulant public import Mathlib.LinearAlgebra.Matrix.ConjTranspose public import Mathlib.LinearAlgebra.Matrix.Defs public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic +public import Mathlib.LinearAlgebra.Matrix.Determinant.Bird public import Mathlib.LinearAlgebra.Matrix.Determinant.Misc public import Mathlib.LinearAlgebra.Matrix.Determinant.TotallyUnimodular public import Mathlib.LinearAlgebra.Matrix.Diagonal @@ -5148,6 +5169,7 @@ public import Mathlib.LinearAlgebra.PID public import Mathlib.LinearAlgebra.PerfectPairing.Basic public import Mathlib.LinearAlgebra.PerfectPairing.Restrict public import Mathlib.LinearAlgebra.Pi +public import Mathlib.LinearAlgebra.PiTensorProduct public import Mathlib.LinearAlgebra.PiTensorProduct.Basic public import Mathlib.LinearAlgebra.PiTensorProduct.Basis public import Mathlib.LinearAlgebra.PiTensorProduct.DFinsupp @@ -5164,6 +5186,8 @@ public import Mathlib.LinearAlgebra.Projectivization.Cardinality public import Mathlib.LinearAlgebra.Projectivization.Collinear public import Mathlib.LinearAlgebra.Projectivization.Constructions public import Mathlib.LinearAlgebra.Projectivization.Independence +public import Mathlib.LinearAlgebra.Projectivization.PSL.PSL2 +public import Mathlib.LinearAlgebra.Projectivization.PSL.Stabilizer public import Mathlib.LinearAlgebra.Projectivization.Subspace public import Mathlib.LinearAlgebra.QuadraticForm.AlgClosed public import Mathlib.LinearAlgebra.QuadraticForm.Basic @@ -5217,6 +5241,7 @@ public import Mathlib.LinearAlgebra.SModEq.Pointwise public import Mathlib.LinearAlgebra.SModEq.Pow public import Mathlib.LinearAlgebra.Semisimple public import Mathlib.LinearAlgebra.SesquilinearForm.Basic +public import Mathlib.LinearAlgebra.SesquilinearForm.Orthogonal public import Mathlib.LinearAlgebra.SesquilinearForm.Star public import Mathlib.LinearAlgebra.Span.Basic public import Mathlib.LinearAlgebra.Span.Defs @@ -5587,6 +5612,7 @@ public import Mathlib.MeasureTheory.Measure.Tilted public import Mathlib.MeasureTheory.Measure.Trim public import Mathlib.MeasureTheory.Measure.Typeclasses.Finite public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms +public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability public import Mathlib.MeasureTheory.Measure.Typeclasses.SFinite public import Mathlib.MeasureTheory.Measure.Typeclasses.ZeroOne @@ -5621,9 +5647,12 @@ public import Mathlib.MeasureTheory.VectorMeasure.Decomposition.JordanSub public import Mathlib.MeasureTheory.VectorMeasure.Decomposition.Lebesgue public import Mathlib.MeasureTheory.VectorMeasure.Decomposition.RadonNikodym public import Mathlib.MeasureTheory.VectorMeasure.Integral +public import Mathlib.MeasureTheory.VectorMeasure.SetIntegral public import Mathlib.MeasureTheory.VectorMeasure.Variation.Basic public import Mathlib.MeasureTheory.VectorMeasure.Variation.Defs +public import Mathlib.MeasureTheory.VectorMeasure.Variation.Semivariation public import Mathlib.MeasureTheory.VectorMeasure.WithDensity +public import Mathlib.MeasureTheory.VectorMeasure.WithDensityVec public import Mathlib.ModelTheory.Algebra.Field.Basic public import Mathlib.ModelTheory.Algebra.Field.CharP public import Mathlib.ModelTheory.Algebra.Field.IsAlgClosed @@ -5890,7 +5919,6 @@ public import Mathlib.NumberTheory.Transcendental.Liouville.LiouvilleWith public import Mathlib.NumberTheory.Transcendental.Liouville.Measure public import Mathlib.NumberTheory.Transcendental.Liouville.Residual public import Mathlib.NumberTheory.TsumDivisorsAntidiagonal -public import Mathlib.NumberTheory.TsumDivsorsAntidiagonal public import Mathlib.NumberTheory.WellApproximable public import Mathlib.NumberTheory.Wilson public import Mathlib.NumberTheory.ZetaValues @@ -5976,6 +6004,7 @@ public import Mathlib.Order.CountableSupClosed public import Mathlib.Order.Cover public import Mathlib.Order.Defs.LinearOrder public import Mathlib.Order.Defs.PartialOrder +public import Mathlib.Order.Defs.Prop public import Mathlib.Order.Defs.Unbundled public import Mathlib.Order.DirSupClosed public import Mathlib.Order.Directed @@ -6248,6 +6277,7 @@ public import Mathlib.Probability.Distributions.Poisson.PoissonLimitThm public import Mathlib.Probability.Distributions.SetBernoulli public import Mathlib.Probability.Distributions.TwoValued public import Mathlib.Probability.Distributions.Uniform +public import Mathlib.Probability.HasCondDistrib public import Mathlib.Probability.HasLaw public import Mathlib.Probability.HasLawExists public import Mathlib.Probability.IdentDistrib @@ -6357,6 +6387,9 @@ public import Mathlib.RepresentationTheory.Equiv public import Mathlib.RepresentationTheory.FDRep public import Mathlib.RepresentationTheory.FinGroupCharZero public import Mathlib.RepresentationTheory.FiniteIndex +public import Mathlib.RepresentationTheory.Homological.ContCohomology.Basic +public import Mathlib.RepresentationTheory.Homological.ContCohomology.Functoriality +public import Mathlib.RepresentationTheory.Homological.ContCohomology.LowDegree public import Mathlib.RepresentationTheory.Homological.FiniteCyclic public import Mathlib.RepresentationTheory.Homological.GroupCohomology.Basic public import Mathlib.RepresentationTheory.Homological.GroupCohomology.FiniteCyclic @@ -6460,6 +6493,7 @@ public import Mathlib.RingTheory.Congruence.BigOperators public import Mathlib.RingTheory.Congruence.Defs public import Mathlib.RingTheory.Congruence.Hom public import Mathlib.RingTheory.Congruence.Opposite +public import Mathlib.RingTheory.Congruence.Star public import Mathlib.RingTheory.Coprime.Basic public import Mathlib.RingTheory.Coprime.Ideal public import Mathlib.RingTheory.Coprime.Lemmas @@ -6478,6 +6512,7 @@ public import Mathlib.RingTheory.DedekindDomain.LinearDisjoint public import Mathlib.RingTheory.DedekindDomain.PID public import Mathlib.RingTheory.DedekindDomain.SInteger public import Mathlib.RingTheory.DedekindDomain.SelmerGroup +public import Mathlib.RingTheory.Depth.Rees public import Mathlib.RingTheory.Derivation.Basic public import Mathlib.RingTheory.Derivation.DifferentialRing public import Mathlib.RingTheory.Derivation.Lie @@ -6497,6 +6532,7 @@ public import Mathlib.RingTheory.EssentialFiniteness public import Mathlib.RingTheory.Etale.Basic public import Mathlib.RingTheory.Etale.Descent public import Mathlib.RingTheory.Etale.Field +public import Mathlib.RingTheory.Etale.Finite public import Mathlib.RingTheory.Etale.Kaehler public import Mathlib.RingTheory.Etale.Locus public import Mathlib.RingTheory.Etale.Pi @@ -6644,8 +6680,10 @@ public import Mathlib.RingTheory.Ideal.Quotient.Index public import Mathlib.RingTheory.Ideal.Quotient.Nilpotent public import Mathlib.RingTheory.Ideal.Quotient.Noetherian public import Mathlib.RingTheory.Ideal.Quotient.Operations +public import Mathlib.RingTheory.Ideal.Quotient.Over public import Mathlib.RingTheory.Ideal.Quotient.PowTransition public import Mathlib.RingTheory.Ideal.Span +public import Mathlib.RingTheory.Ideal.UFD public import Mathlib.RingTheory.IdealFilter.Basic public import Mathlib.RingTheory.IdealFilter.Topology public import Mathlib.RingTheory.Idempotents @@ -6664,8 +6702,11 @@ public import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Defs public import Mathlib.RingTheory.IntegralDomain public import Mathlib.RingTheory.Invariant.Basic public import Mathlib.RingTheory.Invariant.Defs +public import Mathlib.RingTheory.Invariant.Galois public import Mathlib.RingTheory.Invariant.Profinite public import Mathlib.RingTheory.IsAdjoinRoot +public import Mathlib.RingTheory.IsGaloisGroup.Basic +public import Mathlib.RingTheory.IsGaloisGroup.Defs public import Mathlib.RingTheory.IsPrimary public import Mathlib.RingTheory.IsTensorProduct public import Mathlib.RingTheory.Jacobson.Artinian @@ -6714,6 +6755,7 @@ public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Defs public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Square public import Mathlib.RingTheory.LocalRing.Module public import Mathlib.RingTheory.LocalRing.NonLocalRing +public import Mathlib.RingTheory.LocalRing.Pullback public import Mathlib.RingTheory.LocalRing.Quotient public import Mathlib.RingTheory.LocalRing.ResidueField.Basic public import Mathlib.RingTheory.LocalRing.ResidueField.Defs @@ -6782,6 +6824,7 @@ public import Mathlib.RingTheory.MvPowerSeries.NoZeroDivisors public import Mathlib.RingTheory.MvPowerSeries.Order public import Mathlib.RingTheory.MvPowerSeries.PiTopology public import Mathlib.RingTheory.MvPowerSeries.Rename +public import Mathlib.RingTheory.MvPowerSeries.Restricted public import Mathlib.RingTheory.MvPowerSeries.Substitution public import Mathlib.RingTheory.MvPowerSeries.Trunc public import Mathlib.RingTheory.Nakayama @@ -6951,6 +6994,7 @@ public import Mathlib.RingTheory.SimpleModule.WedderburnArtin public import Mathlib.RingTheory.SimpleRing.Basic public import Mathlib.RingTheory.SimpleRing.Congr public import Mathlib.RingTheory.SimpleRing.Defs +public import Mathlib.RingTheory.SimpleRing.DivisionRing public import Mathlib.RingTheory.SimpleRing.Field public import Mathlib.RingTheory.SimpleRing.Matrix public import Mathlib.RingTheory.SimpleRing.Principal @@ -7026,6 +7070,7 @@ public import Mathlib.RingTheory.UniqueFactorizationDomain.Finsupp public import Mathlib.RingTheory.UniqueFactorizationDomain.GCDMonoid public import Mathlib.RingTheory.UniqueFactorizationDomain.Ideal public import Mathlib.RingTheory.UniqueFactorizationDomain.Kaplansky +public import Mathlib.RingTheory.UniqueFactorizationDomain.Localization public import Mathlib.RingTheory.UniqueFactorizationDomain.Moebius public import Mathlib.RingTheory.UniqueFactorizationDomain.Multiplicative public import Mathlib.RingTheory.UniqueFactorizationDomain.Multiplicity @@ -7100,6 +7145,7 @@ public import Mathlib.SetTheory.Cardinal.Defs public import Mathlib.SetTheory.Cardinal.Divisibility public import Mathlib.SetTheory.Cardinal.ENat public import Mathlib.SetTheory.Cardinal.Embedding +public import Mathlib.SetTheory.Cardinal.EventuallyConst public import Mathlib.SetTheory.Cardinal.Finite public import Mathlib.SetTheory.Cardinal.Finsupp public import Mathlib.SetTheory.Cardinal.Free @@ -7143,6 +7189,7 @@ public import Mathlib.SetTheory.ZFC.VonNeumann public import Mathlib.Tactic public import Mathlib.Tactic.Abel public import Mathlib.Tactic.AdaptationNote +public import Mathlib.Tactic.Algebra.AlgebraNF public import Mathlib.Tactic.Algebra.Basic public import Mathlib.Tactic.Algebra.Lemmas public import Mathlib.Tactic.Algebraize @@ -7239,6 +7286,9 @@ public import Mathlib.Tactic.DeriveCountable public import Mathlib.Tactic.DeriveEncodable public import Mathlib.Tactic.DeriveFintype public import Mathlib.Tactic.DeriveTraversable +public import Mathlib.Tactic.Determinant.Bird +public import Mathlib.Tactic.Determinant.Bird.Cert +public import Mathlib.Tactic.Determinant.Bird.Meta public import Mathlib.Tactic.ENatToNat public import Mathlib.Tactic.Eqns public import Mathlib.Tactic.ErwQuestion @@ -7281,6 +7331,7 @@ public import Mathlib.Tactic.GRewrite public import Mathlib.Tactic.GRewrite.Core public import Mathlib.Tactic.GRewrite.Elab public import Mathlib.Tactic.Generalize +public import Mathlib.Tactic.GrindAttrs public import Mathlib.Tactic.Group public import Mathlib.Tactic.GuardGoalNums public import Mathlib.Tactic.GuardHypNums @@ -7398,6 +7449,8 @@ public import Mathlib.Tactic.Order.ToInt public import Mathlib.Tactic.PNatToNat public import Mathlib.Tactic.PPWithUniv public import Mathlib.Tactic.Peel +public import Mathlib.Tactic.Polynomial.Basic +public import Mathlib.Tactic.Polynomial.Core public import Mathlib.Tactic.Polyrith public import Mathlib.Tactic.Positivity public import Mathlib.Tactic.Positivity.Basic @@ -7579,6 +7632,7 @@ public import Mathlib.Topology.Algebra.Module.Determinant public import Mathlib.Topology.Algebra.Module.Equiv public import Mathlib.Topology.Algebra.Module.FiniteDimension public import Mathlib.Topology.Algebra.Module.FiniteDimensionBilinear +public import Mathlib.Topology.Algebra.Module.IsWeak public import Mathlib.Topology.Algebra.Module.LinearMap public import Mathlib.Topology.Algebra.Module.LinearMapPiProd public import Mathlib.Topology.Algebra.Module.LinearPMap @@ -7763,19 +7817,18 @@ public import Mathlib.Topology.Compactness.CompactlyCoherentSpace public import Mathlib.Topology.Compactness.CompactlyGeneratedSpace public import Mathlib.Topology.Compactness.CountablyCompact public import Mathlib.Topology.Compactness.DeltaGeneratedSpace -public import Mathlib.Topology.Compactness.HilbertCubeEmbedding public import Mathlib.Topology.Compactness.Lindelof public import Mathlib.Topology.Compactness.LocallyCompact public import Mathlib.Topology.Compactness.LocallyFinite public import Mathlib.Topology.Compactness.NhdsKer public import Mathlib.Topology.Compactness.Paracompact -public import Mathlib.Topology.Compactness.PseudometrizableLindelof public import Mathlib.Topology.Compactness.SigmaCompact public import Mathlib.Topology.Connected.Basic public import Mathlib.Topology.Connected.CardComponents public import Mathlib.Topology.Connected.Clopen public import Mathlib.Topology.Connected.LocPathConnected public import Mathlib.Topology.Connected.LocallyConnected +public import Mathlib.Topology.Connected.LocallyPathConnected public import Mathlib.Topology.Connected.PathComponentOne public import Mathlib.Topology.Connected.PathConnected public import Mathlib.Topology.Connected.Separation @@ -7821,7 +7874,6 @@ public import Mathlib.Topology.Convenient.GeneratedBy public import Mathlib.Topology.Convenient.HomSpace public import Mathlib.Topology.Convenient.Localization public import Mathlib.Topology.Convenient.OpenClosed -public import Mathlib.Topology.Covering public import Mathlib.Topology.Covering.AddCircle public import Mathlib.Topology.Covering.Basic public import Mathlib.Topology.Covering.Quotient @@ -8055,7 +8107,6 @@ public import Mathlib.Topology.Semicontinuity.Basic public import Mathlib.Topology.Semicontinuity.Defs public import Mathlib.Topology.Semicontinuity.Hemicontinuity public import Mathlib.Topology.Semicontinuity.Lindelof -public import Mathlib.Topology.Semicontinuous public import Mathlib.Topology.SeparatedMap public import Mathlib.Topology.Separation.AlexandrovDiscrete public import Mathlib.Topology.Separation.Basic @@ -8073,6 +8124,7 @@ public import Mathlib.Topology.Separation.Profinite public import Mathlib.Topology.Separation.Regular public import Mathlib.Topology.Separation.SeparatedNhds public import Mathlib.Topology.Sequences +public import Mathlib.Topology.Sets.BaseChangeNhds public import Mathlib.Topology.Sets.Closeds public import Mathlib.Topology.Sets.CompactOpenCovered public import Mathlib.Topology.Sets.Compacts @@ -8093,6 +8145,7 @@ public import Mathlib.Topology.Sheaves.Limits public import Mathlib.Topology.Sheaves.LocalPredicate public import Mathlib.Topology.Sheaves.LocallySurjective public import Mathlib.Topology.Sheaves.MayerVietoris +public import Mathlib.Topology.Sheaves.Module public import Mathlib.Topology.Sheaves.Over public import Mathlib.Topology.Sheaves.PUnit public import Mathlib.Topology.Sheaves.Points diff --git a/Mathlib/Algebra/Algebra/Basic.lean b/Mathlib/Algebra/Algebra/Basic.lean index 9cbe3d32bdbb75..7888df545f1144 100644 --- a/Mathlib/Algebra/Algebra/Basic.lean +++ b/Mathlib/Algebra/Algebra/Basic.lean @@ -113,10 +113,6 @@ theorem coe_algebraMap_ofSubsemiring (S : C) : (algebraMap S R : S → R) = Subt theorem algebraMap_ofSubsemiring_apply (S : C) (x : S) : algebraMap S R x = x := rfl -/-- Algebra over a subring. This builds upon `Subring.module`. -/ -instance ofSubring {R A : Type*} [CommRing R] [Ring A] [Algebra R A] (S : Subring R) : - Algebra S A := inferInstance - theorem algebraMap_ofSubring {R : Type*} [CommRing R] (S : Subring R) : (algebraMap S R : S →+* R) = S.subtype := rfl diff --git a/Mathlib/Algebra/Algebra/Equiv.lean b/Mathlib/Algebra/Algebra/Equiv.lean index bfa2ba100c82b6..9f592f5c7127d0 100644 --- a/Mathlib/Algebra/Algebra/Equiv.lean +++ b/Mathlib/Algebra/Algebra/Equiv.lean @@ -160,12 +160,9 @@ theorem toRingEquiv_eq_coe : e.toRingEquiv = e := lemma toRingEquiv_toRingHom : ((e : A₁ ≃+* A₂) : A₁ →+* A₂) = e := rfl -@[simp] -theorem coe_ringEquiv : ((e : A₁ ≃+* A₂) : A₁ → A₂) = e := - rfl +theorem coe_ringEquiv : ((e : A₁ ≃+* A₂) : A₁ → A₂) = e := rfl -theorem coe_ringEquiv' : (e.toRingEquiv : A₁ → A₂) = e := - rfl +@[deprecated (since := "2026-06-21")] alias coe_ringEquiv' := coe_ringEquiv theorem coe_ringEquiv_injective : Function.Injective ((↑) : (A₁ ≃ₐ[R] A₂) → A₁ ≃+* A₂) := fun _ _ h => ext <| RingEquiv.congr_fun h @@ -190,12 +187,14 @@ theorem toAlgHom_apply (x : A₁) : e.toAlgHom x = e x := rfl @[simp, norm_cast] -theorem coe_algHom : DFunLike.coe e.toAlgHom = DFunLike.coe e := - rfl +theorem coe_toAlgHom : DFunLike.coe e.toAlgHom = e := rfl -theorem coe_algHom_injective : Function.Injective ((↑) : (A₁ ≃ₐ[R] A₂) → A₁ →ₐ[R] A₂) := +theorem coe_toAlgHom_injective : Function.Injective ((↑) : (A₁ ≃ₐ[R] A₂) → A₁ →ₐ[R] A₂) := fun _ _ h => ext <| AlgHom.congr_fun h +@[deprecated (since := "2026-05-05")] alias coe_algHom := coe_toAlgHom +@[deprecated (since := "2026-05-05")] alias coe_algHom_injective := coe_toAlgHom_injective + @[simp, norm_cast] lemma toAlgHom_toRingHom : ((e : A₁ →ₐ[R] A₂) : A₁ →+* A₂) = e := rfl @@ -479,15 +478,18 @@ def ofAlgHom (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ : f.comp left_inv := AlgHom.ext_iff.1 h₂ right_inv := AlgHom.ext_iff.1 h₁ } -theorem coe_algHom_ofAlgHom (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) : +theorem toAlgHom_ofAlgHom (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) : ↑(ofAlgHom f g h₁ h₂) = f := rfl @[simp] -theorem ofAlgHom_coe_algHom (f : A₁ ≃ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) : +theorem ofAlgHom_toAlgHom (f : A₁ ≃ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) : ofAlgHom (↑f) g h₁ h₂ = f := ext fun _ => rfl +@[deprecated (since := "2026-05-05")] alias coe_algHom_ofAlgHom := toAlgHom_ofAlgHom +@[deprecated (since := "2026-05-05")] alias ofAlgHom_coe_algHom := ofAlgHom_toAlgHom + theorem ofAlgHom_symm (f : A₁ →ₐ[R] A₂) (g : A₂ →ₐ[R] A₁) (h₁ h₂) : (ofAlgHom f g h₁ h₂).symm = ofAlgHom g f h₂ h₁ := rfl @@ -764,7 +766,7 @@ def algHomUnitsEquiv (R S : Type*) [CommSemiring R] [Semiring S] [Algebra R S] : /-- See also `Finite.algHom` -/ instance _root_.Finite.algEquiv [Finite (A₁ →ₐ[R] A₂)] : Finite (A₁ ≃ₐ[R] A₂) := - Finite.of_injective _ AlgEquiv.coe_algHom_injective + Finite.of_injective _ AlgEquiv.coe_toAlgHom_injective -- TODO Morally this is just `isLocalHom_equiv`: can we obviate the need for this instance? instance : IsLocalHom e.toAlgHom := by diff --git a/Mathlib/Algebra/Algebra/Operations.lean b/Mathlib/Algebra/Algebra/Operations.lean index 54267e7ffb5618..d31f10ca1de085 100644 --- a/Mathlib/Algebra/Algebra/Operations.lean +++ b/Mathlib/Algebra/Algebra/Operations.lean @@ -367,6 +367,16 @@ lemma restrictScalars_pow {A B C : Type*} [Semiring A] [Semiring B] | n + 2, _ => by simp [Submodule.pow_succ (n := n + 1), restrictScalars_mul, restrictScalars_pow n.succ_ne_zero] +instance instIsReduced [IsReduced A] : IsReduced (Submodule R A) where + eq_zero M hM := by + rw [Submodule.zero_eq_bot, Submodule.eq_bot_iff] + rintro m hm + obtain ⟨n, hn⟩ := hM + exact eq_zero_of_pow_eq_zero <| (M ^ n).eq_bot_iff.mp hn _ (pow_mem_pow M hm n) + +theorem pow_eq_bot [IsReduced A] {M : Submodule R A} {n : ℕ} (hn : n ≠ 0) : + M ^ n = ⊥ ↔ M = ⊥ := by refine ⟨eq_zero_of_pow_eq_zero, by aesop⟩ + end Module variable {ι : Sort uι} diff --git a/Mathlib/Algebra/Algebra/Pi.lean b/Mathlib/Algebra/Algebra/Pi.lean index 28037ee3dd8ca1..cf9e8606cde360 100644 --- a/Mathlib/Algebra/Algebra/Pi.lean +++ b/Mathlib/Algebra/Algebra/Pi.lean @@ -48,15 +48,41 @@ theorem algebraMap_def (a : R) : algebraMap R (Π i, A i) a = fun i ↦ algebraM theorem algebraMap_apply (a : R) (i : ι) : algebraMap R (Π i, A i) a i = algebraMap R (A i) a := rfl -variable {ι} (R) +variable {ι} -/-- A family of algebra homomorphisms `g i : B →ₐ[R] A i` defines a ring homomorphism -`Pi.algHom g : B →ₐ[R] Π i, A i` given by `Pi.algHom g x i = g i x`. -/ +variable {A} in +/-- A family of algebra homomorphisms `g i : B →ₐ[R] A i` defines an algebra homomorphism +`AlgHom.pi g : B →ₐ[R] Π i, A i` given by `AlgHom.pi g x i = g i x`. -/ @[simps!] -def algHom {B : Type*} [Semiring B] [Algebra R B] (g : ∀ i, B →ₐ[R] A i) : B →ₐ[R] Π i, A i where +def _root_.AlgHom.pi {B : Type*} [Semiring B] [Algebra R B] (g : Π i, B →ₐ[R] A i) : + B →ₐ[R] Π i, A i where __ := RingHom.pi fun i ↦ (g i).toRingHom commutes' r := by ext; simp +variable {A} in +/-- `AlgHom.pi` commutes with composition. -/ +theorem _root_.AlgHom.pi_comp {B C : Type*} [Semiring B] [Algebra R B] [Semiring C] [Algebra R C] + (g : ∀ i, C →ₐ[R] A i) (h : B →ₐ[R] C) : + (AlgHom.pi g).comp h = AlgHom.pi (fun i ↦ (g i).comp h) := rfl + +variable (R) + +/-- Use `AlgHom.pi` instead. -/ +@[deprecated AlgHom.pi (since := "2026-05-30")] +abbrev algHom {B : Type*} [Semiring B] [Algebra R B] (g : Π i, B →ₐ[R] A i) : B →ₐ[R] Π i, A i := + .pi g + +/-- Use `AlgHom.pi_apply` instead. -/ +@[deprecated AlgHom.pi_apply (since := "2026-05-30")] +theorem algHom_apply {B : Type*} [Semiring B] [Algebra R B] + (g : Π i, B →ₐ[R] A i) (x : B) (i : ι) : Pi.algHom R A g x i = g i x := + AlgHom.pi_apply g x i + +@[deprecated AlgHom.pi_comp (since := "2026-05-30")] +theorem algHom_comp {B C : Type*} [Semiring B] [Algebra R B] [Semiring C] [Algebra R C] + (g : ∀ i, C →ₐ[R] A i) (h : B →ₐ[R] C) : + (algHom R A g).comp h = algHom R A (fun i ↦ (g i).comp h) := rfl + /-- `Function.eval` as an `AlgHom`. The name matches `Pi.evalRingHom`, `Pi.evalMonoidHom`, etc. -/ @[simps] @@ -68,12 +94,11 @@ def evalAlgHom (i : ι) : (Π i, A i) →ₐ[R] A i := lemma coe_evalAlgHom (i : ι) : evalAlgHom R A i = evalRingHom A i := rfl @[simp] -theorem algHom_evalAlgHom : algHom R A (evalAlgHom R A) = AlgHom.id R (Π i, A i) := rfl +theorem _root_.AlgHom.pi_evalAlgHom : AlgHom.pi (evalAlgHom R A) = AlgHom.id R (Π i, A i) := + rfl -/-- `Pi.algHom` commutes with composition. -/ -theorem algHom_comp {B C : Type*} [Semiring B] [Algebra R B] [Semiring C] [Algebra R C] - (g : ∀ i, C →ₐ[R] A i) (h : B →ₐ[R] C) : - (algHom R A g).comp h = algHom R A (fun i ↦ (g i).comp h) := rfl +@[deprecated (since := "2026-06-03")] +alias algHom_evalAlgHom := _root_.AlgHom.pi_evalAlgHom variable (S : ι → Type*) [∀ i, CommSemiring (S i)] diff --git a/Mathlib/Algebra/Algebra/RestrictScalars.lean b/Mathlib/Algebra/Algebra/RestrictScalars.lean index 3e2a3022589112..0fadc0e547779e 100644 --- a/Mathlib/Algebra/Algebra/RestrictScalars.lean +++ b/Mathlib/Algebra/Algebra/RestrictScalars.lean @@ -128,7 +128,7 @@ theorem IsScalarTower.restrictScalars [Module S M] : /-- This instance is only relevant when `RestrictScalars.moduleOrig` is available as an instance. -/ instance RestrictScalars.isScalarTower [Module S M] : IsScalarTower R S (RestrictScalars R S M) := - IsScalarTower.restrictScalars R S M + IsScalarTower.restrictScalars R S M end diff --git a/Mathlib/Algebra/Algebra/Spectrum/Basic.lean b/Mathlib/Algebra/Algebra/Spectrum/Basic.lean index 90ae07b320afb9..4970815af4ca2f 100644 --- a/Mathlib/Algebra/Algebra/Spectrum/Basic.lean +++ b/Mathlib/Algebra/Algebra/Spectrum/Basic.lean @@ -13,6 +13,7 @@ public import Mathlib.Tactic.NoncommRing /-! # Spectrum of an element in an algebra + This file develops the basic theory of the spectrum of an element of an algebra. This theory will serve as the foundation for spectral theory in Banach algebras. @@ -431,7 +432,7 @@ theorem AlgEquiv.spectrum_eq {F R A B : Type*} [CommSemiring R] [Ring A] [Ring B [Algebra R B] [EquivLike F A B] [AlgEquivClass F R A B] (f : F) (a : A) : spectrum R (f a) = spectrum R a := Set.Subset.antisymm (AlgHom.spectrum_apply_subset _ _) <| by - simpa only [AlgEquiv.coe_algHom, AlgEquiv.coe_coe_symm_apply_coe_apply] using + simpa only [AlgEquiv.coe_toAlgHom, AlgEquiv.coe_coe_symm_apply_coe_apply] using AlgHom.spectrum_apply_subset (AlgEquivClass.toAlgEquiv f : A ≃ₐ[R] B).symm (f a) section ConjugateUnits diff --git a/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean b/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean index 50fffbc6381b3c..85f0870d237eb7 100644 --- a/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean +++ b/Mathlib/Algebra/Algebra/Subalgebra/Centralizer.lean @@ -117,7 +117,7 @@ lemma centralizer_coe_image_includeRight_eq_center_tensorProduct ⟨fun h b hb ↦ (Algebra.TensorProduct.comm R A B).symm.injective <| by aesop, fun h b hb ↦ (Algebra.TensorProduct.comm R A B).injective <| by aesop⟩ · ext x - simp only [AlgHom.mem_range, mem_comap, AlgEquiv.coe_algHom] + simp only [AlgHom.mem_range, mem_comap, AlgEquiv.coe_toAlgHom] constructor · rintro ⟨x, rfl⟩ exact ⟨(Algebra.TensorProduct.comm R _ _) x, diff --git a/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean b/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean index c3c9982f336797..69b915e51330eb 100644 --- a/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean +++ b/Mathlib/Algebra/Algebra/Subalgebra/Prod.lean @@ -25,7 +25,9 @@ namespace Subalgebra open Algebra -variable {R A B : Type*} [CommSemiring R] [Semiring A] [Algebra R A] [Semiring B] [Algebra R B] +variable {R A B C D : Type*} [CommSemiring R] [Semiring A] [Algebra R A] [Semiring B] [Algebra R B] + [Semiring C] [Algebra R C] [Semiring D] [Algebra R D] + variable (S : Subalgebra R A) (S₁ : Subalgebra R B) /-- The product of two subalgebras is a subalgebra. -/ @@ -60,4 +62,9 @@ theorem prod_inf_prod {S T : Subalgebra R A} {S₁ T₁ : Subalgebra R B} : protected theorem center_prod : center R (A × B) = prod (center R A) (center R B) := SetLike.coe_injective Set.center_prod +@[simp] +theorem _root_.AlgHom.range_prodMap (f : A →ₐ[R] B) (g : C →ₐ[R] D) : + (f.prodMap g).range = f.range.prod g.range := + SetLike.coe_injective Set.range_prodMap + end Subalgebra diff --git a/Mathlib/Algebra/Algebra/Tower.lean b/Mathlib/Algebra/Algebra/Tower.lean index 16847ed505be3f..464f3228d15a2c 100644 --- a/Mathlib/Algebra/Algebra/Tower.lean +++ b/Mathlib/Algebra/Algebra/Tower.lean @@ -204,19 +204,27 @@ section variable {R} -/-- Any `f : A →ₐ[R] B` is also an `R ⧸ I`-algebra homomorphism if the `R`-algebra structure on -`A` and `B` factors via `R ⧸ I`. -/ -@[simps! apply] -def extendScalarsOfSurjective (h : Function.Surjective (algebraMap R S)) - (f : A →ₐ[R] B) : A →ₐ[S] B where - toRingHom := f - commutes' := by simp [h.forall, ← IsScalarTower.algebraMap_apply] +/-- Any `f : A →ₐ[R] B` is also an `S`-algebra homomorphism if the `R`-algebra structure on +`A` and `B` factors via a surjective ring homomorphism `R →+* S`. -/ +@[simps! apply symm_apply] +def extendScalarsOfSurjective (h : Function.Surjective (algebraMap R S)) : + (A →ₐ[R] B) ≃ (A →ₐ[S] B) where + toFun f := { f with commutes' := by simp [h.forall, ← IsScalarTower.algebraMap_apply] } + invFun := restrictScalars R @[simp] lemma restrictScalars_extendScalarsOfSurjective (h : Function.Surjective (algebraMap R S)) (f : A →ₐ[R] B) : (f.extendScalarsOfSurjective h).restrictScalars R = f := rfl +/-- Any `f : A →ₐ[R] B` is also an `S`-algebra homomorphism if the `R`-algebra structure on +`A` and `B` factors via a surjective ring homomorphism `R →+* S`. -/ +@[simps! apply symm_apply] +def extendScalarsHomOfSurjective (h : Function.Surjective (algebraMap R S)) : + (A →ₐ[R] A) ≃* (A →ₐ[S] A) where + __ := extendScalarsOfSurjective h + map_mul' _ _ := rfl + end end AlgHom @@ -263,12 +271,13 @@ section variable {R} -/-- Any `f : A ≃ₐ[R] B` is also an `R ⧸ I`-algebra isomorphism if the `R`-algebra structure on -`A` and `B` factors via `R ⧸ I`. -/ -def extendScalarsOfSurjective (h : Function.Surjective (algebraMap R S)) - (f : A ≃ₐ[R] B) : A ≃ₐ[S] B where - toRingEquiv := f - commutes' := (f.toAlgHom.extendScalarsOfSurjective h).commutes' +/-- Any `f : A ≃ₐ[R] B` is also an `S`-algebra isomorphism if the `R`-algebra structure on +`A` and `B` factors via a surjective ring homomorphism `R →+* S`. -/ +@[simps! apply symm_apply] +def extendScalarsOfSurjective (h : Function.Surjective (algebraMap R S)) : + (A ≃ₐ[R] B) ≃ A ≃ₐ[S] B where + toFun f := { f with commutes' := (f.toAlgHom.extendScalarsOfSurjective h).commutes' } + invFun := AlgEquiv.restrictScalars R @[simp] lemma coe_extendScalarsOfSurjective (h : Function.Surjective (algebraMap R S)) (f : A ≃ₐ[R] B) : ⇑(extendScalarsOfSurjective h f) = f := rfl @@ -283,6 +292,14 @@ lemma extendScalarsOfSurjective_symm (h : Function.Surjective (algebraMap R S)) (f : A ≃ₐ[R] B) : (f.extendScalarsOfSurjective h).symm = f.symm.extendScalarsOfSurjective h := rfl +/-- Any `f : A ≃ₐ[R] B` is also an `S`-algebra isomorphism if the `R`-algebra structure on +`A` and `B` factors via a surjective ring homomorphism `R →+* S`. -/ +@[simps! apply symm_apply] +def extendScalarsHomOfSurjective (h : Function.Surjective ⇑(algebraMap R S)) : + (A ≃ₐ[R] A) ≃* (A ≃ₐ[S] A) where + __ := extendScalarsOfSurjective h + map_mul' _ _ := rfl + end end AlgEquiv diff --git a/Mathlib/Algebra/Azumaya/Basic.lean b/Mathlib/Algebra/Azumaya/Basic.lean index cf682e3afca522..8f5035854b7b00 100644 --- a/Mathlib/Algebra/Azumaya/Basic.lean +++ b/Mathlib/Algebra/Azumaya/Basic.lean @@ -79,7 +79,7 @@ theorem of_AlgEquiv (e : A ≃ₐ[R] B) [IsAzumaya R A] : IsAzumaya R B := let _ : Module.Finite R B := .equiv e.toLinearEquiv ⟨Function.Bijective.of_comp_iff (AlgHom.mulLeftRight R B) (Algebra.TensorProduct.congr e e.op).bijective |>.1 <| by - rw [← AlgEquiv.coe_algHom, ← AlgHom.coe_comp, mulLeftRight_comp_congr] + rw [← AlgEquiv.coe_toAlgHom, ← AlgHom.coe_comp, mulLeftRight_comp_congr] simp [AlgHom.mulLeftRight_bij]⟩ end IsAzumaya diff --git a/Mathlib/Algebra/BigOperators/Group/List/Basic.lean b/Mathlib/Algebra/BigOperators/Group/List/Basic.lean index 1f5a5ef9210ca9..525adfce9ad649 100644 --- a/Mathlib/Algebra/BigOperators/Group/List/Basic.lean +++ b/Mathlib/Algebra/BigOperators/Group/List/Basic.lean @@ -249,7 +249,6 @@ lemma prod_map_erase [DecidableEq α] (f : α → M) {a} : @[to_additive] lemma Perm.prod_eq (h : Perm l₁ l₂) : prod l₁ = prod l₂ := h.foldr_op_eq -set_option linter.existingAttributeWarning false in attribute [to_additive existing] prod_reverse @[to_additive] diff --git a/Mathlib/Algebra/BigOperators/Group/List/Defs.lean b/Mathlib/Algebra/BigOperators/Group/List/Defs.lean index dc2a0d7b276cc6..c6417cc1f53bc5 100644 --- a/Mathlib/Algebra/BigOperators/Group/List/Defs.lean +++ b/Mathlib/Algebra/BigOperators/Group/List/Defs.lean @@ -23,7 +23,6 @@ variable {ι M N : Type*} namespace List section Defs -set_option linter.existingAttributeWarning false in attribute [to_additive existing] prod prod_nil prod_cons prod_one_cons prod_append prod_concat prod_flatten prod_eq_foldl diff --git a/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean b/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean index 3a1586e333a224..c3e776db575321 100644 --- a/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean +++ b/Mathlib/Algebra/BigOperators/NatAntidiagonal.lean @@ -20,6 +20,8 @@ variable {M N : Type*} [CommMonoid M] [AddCommMonoid N] namespace Finset +open HasAntidiagonal + namespace Nat theorem prod_antidiagonal_succ {n : ℕ} {f : ℕ × ℕ → M} : diff --git a/Mathlib/Algebra/Category/AlgCat/Basic.lean b/Mathlib/Algebra/Category/AlgCat/Basic.lean index e2ac960488e9b5..5ce0fe4c889ec0 100644 --- a/Mathlib/Algebra/Category/AlgCat/Basic.lean +++ b/Mathlib/Algebra/Category/AlgCat/Basic.lean @@ -252,3 +252,83 @@ instance AlgCat.forget_reflects_isos : (forget (AlgCat.{v} R)).ReflectsIsomorphi let i := asIso ((forget (AlgCat.{v} R)).map f) let e : X ≃ₐ[R] Y := { f.hom, i.toEquiv with } exact e.toAlgebraIso.isIso_hom + +namespace AlgCat + +/-- The restriction of scalars functor `AlgCat S ⥤ AlgCat R` induced by a ring homomorphism +`R →+* S`. -/ +@[simps] +def restrictScalars {R S : Type*} [CommRing R] [CommRing S] (f : R →+* S) : + AlgCat.{v} S ⥤ AlgCat.{v} R where + obj A := + letI : Algebra R A := Algebra.compHom _ f + AlgCat.of R A + map {A B} g := + letI : Algebra R A := Algebra.compHom _ f + letI : Algebra R B := Algebra.compHom _ f + letI : Algebra R S := f.toAlgebra + haveI : IsScalarTower R S A := .of_algebraMap_eq' rfl + haveI : IsScalarTower R S B := .of_algebraMap_eq' rfl + AlgCat.ofHom (g.hom.restrictScalars _) + +-- The option makes `simps` produce the correct lemmas +set_option backward.isDefEq.respectTransparency false in +/-- Restricting scalars along the identity is isomorphic to the identity. -/ +@[simps!] +def restrictScalarsId' {R : Type*} [CommRing R] (f : R →+* R) (hf : f = .id R) : + AlgCat.restrictScalars.{v} f ≅ 𝟭 _ := + NatIso.ofComponents + fun A ↦ AlgEquiv.toAlgebraIso <| + @AlgEquiv.ofRingEquiv (f := RingEquiv.refl _) _ _ _ _ _ _ + ((restrictScalars f).obj A).isAlgebra _ fun _ ↦ by subst hf; rfl + +-- The option makes `simps` produce the correct lemmas +set_option backward.isDefEq.respectTransparency false in +/-- Restricting scalars along a composition is isomorphic to the composition +of restriction of scalars. -/ +@[simps!] +def restrictScalarsComp' {R S T : Type*} [CommRing R] [CommRing S] [CommRing T] (f : R →+* S) + (g : S →+* T) (gf : R →+* T) (hfg : gf = g.comp f) : + AlgCat.restrictScalars.{v} gf ≅ + AlgCat.restrictScalars.{v} g ⋙ AlgCat.restrictScalars.{v} f := + NatIso.ofComponents + fun A ↦ AlgEquiv.toAlgebraIso <| + @AlgEquiv.ofRingEquiv (f := RingEquiv.refl _) _ _ _ _ _ _ + ((restrictScalars gf).obj A).isAlgebra + ((restrictScalars f).obj ((restrictScalars g).obj A)).isAlgebra + fun _ ↦ by subst hfg; rfl + +/-- A ring isomorphism induces an equivalence of categories of algebras. -/ +@[simps] +def restrictScalarsEquivalenceOfRingEquiv {R S : Type*} [CommRing R] [CommRing S] (e : R ≃+* S) : + AlgCat.{u} S ≌ AlgCat.{u} R where + functor := restrictScalars e.toRingHom + inverse := restrictScalars e.symm.toRingHom + unitIso := (restrictScalarsId' _ rfl).symm ≪≫ + restrictScalarsComp' _ _ _ e.toRingHom_comp_symm_toRingHom.symm + counitIso := (restrictScalarsComp' _ _ _ e.symm_toRingHom_comp_toRingHom.symm).symm ≪≫ + restrictScalarsId' _ rfl + +instance {R S : Type*} [CommRing R] [CommRing S] (e : R ≃+* S) : + (restrictScalars e.toRingHom).IsEquivalence := + inferInstanceAs <| (restrictScalarsEquivalenceOfRingEquiv e).functor.IsEquivalence + +instance {R S : Type*} [CommRing R] [CommRing S] (e : R ≃+* S) : + (restrictScalars e.symm.toRingHom).IsEquivalence := + inferInstanceAs <| (restrictScalarsEquivalenceOfRingEquiv e).inverse.IsEquivalence + +/-- The equivalence of categories of `ℤ`-algebras and rings. -/ +@[simps! (dsimpLhs := true) functor inverse_obj inverse_map_hom unitIso_hom_app_hom_apply counitIso] +def intEquivalence : AlgCat.{u} ℤ ≌ RingCat.{u} where + functor := forget₂ _ _ + inverse.obj A := AlgCat.of ℤ A + inverse.map f := AlgCat.ofHom f.hom.toIntAlgHom + unitIso := NatIso.ofComponents + fun A ↦ AlgEquiv.toAlgebraIso (@.ofRingEquiv (f := RingEquiv.refl _) + _ _ _ _ _ _ _ (Ring.toIntAlgebra _) fun _ ↦ by simp) + counitIso := Iso.refl _ + +instance : (forget₂ (AlgCat.{u} ℤ) RingCat.{u}).IsEquivalence := + inferInstanceAs <| intEquivalence.functor.IsEquivalence + +end AlgCat diff --git a/Mathlib/Algebra/Category/AlgCat/TensorAlgebra.lean b/Mathlib/Algebra/Category/AlgCat/TensorAlgebra.lean new file mode 100644 index 00000000000000..8c91ec30bdbcae --- /dev/null +++ b/Mathlib/Algebra/Category/AlgCat/TensorAlgebra.lean @@ -0,0 +1,77 @@ +/- +Copyright (c) 2026 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Merten +-/ +module + +public import Mathlib.Algebra.Category.AlgCat.Basic +public import Mathlib.Algebra.Category.Grp.ZModuleEquivalence +public import Mathlib.Algebra.Category.ModuleCat.ChangeOfRings +public import Mathlib.Algebra.Ring.Shrink +public import Mathlib.LinearAlgebra.TensorAlgebra.Basic + +/-! +# `TensorAlgebra` as a functor `ModuleCat R ⥤ AlgCat R` + +In this file we define the functor `AlgCat.tensorAlgebra : ModuleCat R ⥤ AlgCat R` +sending an `R`-module `M` to the tensor algebra `TensorAlgebra R M`. We +show that this functor is a left-adjoint to the forgetful functor `AlgCat R ⥤ ModuleCat R`. +-/ + +@[expose] public section + +universe w v u + +open CategoryTheory + +namespace AlgCat + +/-- The functor sending an `R`-module `M` to its tensor algebra over `R`. -/ +@[simps] +def tensorAlgebra (R : Type u) [CommRing R] : ModuleCat.{w} R ⥤ AlgCat.{max u w} R where + obj M := AlgCat.of R (TensorAlgebra R M) + map f := AlgCat.ofHom (TensorAlgebra.lift _ (TensorAlgebra.ι _ ∘ₗ f.hom)) + +variable (R : Type u) [CommRing R] + +set_option backward.isDefEq.respectTransparency false in +set_option backward.defeqAttrib.useBackward true in +/-- Taking the tensor algebra forms a left adjoint of the forgetful functor from `AlgCat R` to +`ModuleCat R`. -/ +@[simps] +def tensorAlgebraAdj : tensorAlgebra.{u} R ⊣ forget₂ (AlgCat.{u} R) (ModuleCat.{u} R) where + unit.app M := ModuleCat.ofHom (TensorAlgebra.ι _) + counit.app A := AlgCat.ofHom (TensorAlgebra.lift R .id) + counit.naturality _ _ _ := by + ext : 1 + apply TensorAlgebra.hom_ext + ext + simp + left_triangle_components _ := by + ext : 1 + dsimp + ext + simp + +set_option backward.isDefEq.respectTransparency false in +instance (R : Type v) [CommRing R] [Small.{u} R] : + (forget₂ (AlgCat.{u} R) (ModuleCat.{u} R)).IsRightAdjoint := by + let e : AlgCat.{u} R ≌ AlgCat.{u} (Shrink.{u} R) := + restrictScalarsEquivalenceOfRingEquiv (Shrink.ringEquiv R) + have : e.inverse ⋙ forget₂ (AlgCat R) (ModuleCat R) = forget₂ _ _ ⋙ + (ModuleCat.restrictScalarsEquivalenceOfRingEquiv (Shrink.ringEquiv R)).inverse := + rfl + rw [← Functor.isRightAdjoint_comp_iff_right e.inverse, this] + have := (tensorAlgebraAdj (Shrink.{u} R)).isRightAdjoint + infer_instance + +instance : (forget₂ RingCat.{u} AddCommGrpCat.{u}).IsRightAdjoint := by + rw [← Functor.isRightAdjoint_comp_iff_right (forget₂ (AlgCat.{u} ℤ) RingCat.{u})] + have heq : forget₂ (AlgCat.{u} ℤ) _ ⋙ forget₂ (ModuleCat.{u} ℤ) AddCommGrpCat.{u} = + forget₂ (AlgCat.{u} ℤ) RingCat.{u} ⋙ forget₂ RingCat.{u} AddCommGrpCat.{u} := + rfl + rw [← heq] + infer_instance + +end AlgCat diff --git a/Mathlib/Algebra/Category/ContinuousCohomology/Basic.lean b/Mathlib/Algebra/Category/ContinuousCohomology/Basic.lean deleted file mode 100644 index 266fd78fe69811..00000000000000 --- a/Mathlib/Algebra/Category/ContinuousCohomology/Basic.lean +++ /dev/null @@ -1,215 +0,0 @@ -/- -Copyright (c) 2025 Richard Hill. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Richard Hill, Andrew Yang --/ -module - -public import Mathlib.Algebra.Category.ModuleCat.Topology.Homology -public import Mathlib.Algebra.Homology.Embedding.Restriction -public import Mathlib.Algebra.Homology.Functor -public import Mathlib.Algebra.Homology.ShortComplex.HomologicalComplex -public import Mathlib.CategoryTheory.Action.Limits -public import Mathlib.Topology.ContinuousMap.Algebra - -/-! - -# Continuous cohomology - -We define continuous cohomology as the homology of homogeneous cochains. - -## Implementation details - -We define homogeneous cochains as `g`-invariant continuous function in `C(G, C(G,...,C(G, M)))` -instead of the usual `C(Gⁿ, M)` to allow more general topological groups other than locally compact -ones. For this to work, we also work in `Action (TopModuleCat R) G`, where the `G` action on `M` -is only continuous on `M`, and not necessarily continuous in both variables, because the `G` action -on `C(G, M)` might not be continuous on both variables even if it is on `M`. - -For the differential map, instead of a finite sum we use the inductive definition -`d₋₁ : M → C(G, M) := const : m ↦ g ↦ m` and -`dₙ₊₁ : C(G, _) → C(G, C(G, _)) := const - C(G, dₙ) : f ↦ g ↦ f - dₙ (f (g))` -See `ContinuousCohomology.MultiInd.d`. - -## Main definition -- `ContinuousCohomology.homogeneousCochains`: - The functor taking an `R`-linear `G`-representation to the complex of homogeneous cochains. -- `continuousCohomology`: - The functor taking an `R`-linear `G`-representation to its `n`-th continuous cohomology. - -## TODO -- Show that it coincides with `groupCohomology` for discrete groups. -- Give the usual description of cochains in terms of `n`-ary functions for locally compact groups. -- Show that short exact sequences induce long exact sequences in certain scenarios. --/ - -set_option backward.defeqAttrib.useBackward true - -@[expose] public section - -open CategoryTheory Functor ContinuousMap - -variable (R G : Type*) [CommRing R] [Group G] [TopologicalSpace R] - -namespace ContinuousCohomology - -variable [TopologicalSpace G] [IsTopologicalGroup G] - -variable {R G} in -/-- The `G` representation `C(G, rep)` given a representation `rep`. -The `G` action is defined by `g • f := x ↦ g • f (g⁻¹ * x)`. -/ -abbrev Iobj (rep : Action (TopModuleCat R) G) : Action (TopModuleCat R) G where - V := .of R C(G, rep.V) - ρ := - { toFun g := TopModuleCat.ofHom - { toFun f := .comp (rep.ρ g).hom (f.comp (Homeomorph.mulLeft g⁻¹)) - map_add' _ _ := by ext; simp - map_smul' _ _ := by ext; simp } - map_one' := ConcreteCategory.ext (by ext; simp) - map_mul' _ _ := ConcreteCategory.ext (by ext; simp [mul_assoc]) } - -lemma Iobj_ρ_apply (rep : Action (TopModuleCat R) G) (g f x) : - ((Iobj rep).ρ g).hom f x = (rep.ρ g).hom (f (g⁻¹ * x)) := rfl - -/-- The functor taking a representation `rep` to the representation `C(G, rep)`. -/ -@[simps] -def I : Action (TopModuleCat R) G ⥤ Action (TopModuleCat R) G where - obj := Iobj - map {M N} φ := - { hom := TopModuleCat.ofHom (ContinuousLinearMap.compLeftContinuous _ _ φ.hom.hom) - comm g := by - ext f g' - change (M.ρ g ≫ φ.hom).hom (f (g⁻¹ * g')) = (φ.hom ≫ N.ρ g).hom (f (g⁻¹ * g')) - rw [φ.comm] } - map_id _ := rfl - map_comp _ _ := rfl - -instance : (I R G).Additive where -instance : (I R G).Linear R where - -/-- The constant function `rep ⟶ C(G, rep)` as a natural transformation. -/ -@[simps] -def const : 𝟭 _ ⟶ I R G where - app _ := { hom := TopModuleCat.ofHom (.const _ _), comm _ := rfl } - naturality _ _ _ := rfl - -namespace MultiInd - -set_option linter.style.whitespace false in -- manual alignment is not recognised -/-- The n-th functor taking `M` to `C(G, C(G,...,C(G, M)))` (with n `G`s). -These functors form a complex, see `MultiInd.complex`. -/ -def functor : ℕ → Action (TopModuleCat R) G ⥤ Action (TopModuleCat R) G - | 0 => 𝟭 _ - | n + 1 => functor n ⋙ I R G - -set_option linter.style.whitespace false in -- manual alignment is not recognised -/-- The differential map in `MultiInd.complex`. -/ -def d : ∀ n : ℕ, functor R G n ⟶ functor R G (n + 1) - | 0 => const R G - | n + 1 => whiskerLeft (functor R G (n + 1)) (const R G) - (by exact whiskerRight (d n) (I R G)) - -lemma d_zero : d R G 0 = const R G := rfl - -lemma d_succ (n : ℕ) : - d R G (n + 1) = whiskerLeft (functor R G (n + 1)) (const R G) - - (by exact whiskerRight (d R G n) (I R G)) := rfl - -set_option backward.isDefEq.respectTransparency false in -@[reassoc (attr := simp)] -lemma d_comp_d (n : ℕ) : - d R G n ≫ d R G (n + 1) = 0 := by - induction n with - | zero => - rw [d_succ, Preadditive.comp_sub, sub_eq_zero] - rfl - | succ n ih => - rw [d_succ R G (n + 1), Preadditive.comp_sub] - nth_rw 2 [d_succ] - rw [Preadditive.sub_comp, ← whiskerRight_comp, ih, - Functor.whiskerRight_zero, sub_zero, sub_eq_zero] - rfl - -/-- The complex of functors whose behaviour pointwise takes an `R`-linear `G`-representation `M` -to the complex `M → C(G, M) → ⋯ → C(G, C(G,...,C(G, M))) → ⋯` -The `G`-invariant submodules of it is the homogeneous cochains (shifted by one). -/ -def complex : CochainComplex (Action (TopModuleCat R) G ⥤ Action (TopModuleCat R) G) ℕ := - CochainComplex.of (functor R G) (d R G) (d_comp_d R G) - -end MultiInd - -/-- The functor taking an `R`-linear `G`-representation to its `G`-invariant submodule. -/ -def invariants : Action (TopModuleCat R) G ⥤ TopModuleCat R where - obj M := .of R - { carrier := { x | ∀ g : G, (M.ρ g).hom x = x } - add_mem' hx hy g := by simp [hx g, hy g] - zero_mem' := by simp - smul_mem' r x hx g := by simp [hx g] : Submodule R M.V } - map f := TopModuleCat.ofHom <| f.hom.hom.restrict fun x hx g ↦ - congr($(f.comm g) x).symm.trans congr(f.hom.hom $(hx g)) - -instance : (invariants R G).Linear R where -instance : (invariants R G).Additive where - -/-- `homogeneousCochains R G` is the functor taking -an `R`-linear `G`-representation to the complex of homogeneous cochains. -/ -def homogeneousCochains : Action (TopModuleCat R) G ⥤ CochainComplex (TopModuleCat R) ℕ := - (MultiInd.complex R G).asFunctor ⋙ (invariants R G).mapHomologicalComplex _ ⋙ - (ComplexShape.embeddingUp'Add 1 1).restrictionFunctor _ - -/-- `continuousCohomology R G n` is the functor taking -an `R`-linear `G`-representation to its `n`-th continuous cohomology. -/ -noncomputable -def _root_.continuousCohomology (n : ℕ) : Action (TopModuleCat R) G ⥤ TopModuleCat R := - homogeneousCochains R G ⋙ HomologicalComplex.homologyFunctor _ _ n - -set_option backward.isDefEq.respectTransparency false in -/-- The `0`-homogeneous cochains are isomorphic to `Xᴳ`. -/ -def kerHomogeneousCochainsZeroEquiv - (X : Action (TopModuleCat R) G) (n : ℕ) (hn : n = 1) : - (((homogeneousCochains R G).obj X).d 0 n).hom.ker ≃L[R] (invariants R G).obj X where - toFun x := - { val := DFunLike.coe (F := C(G, _)) x.1.1 1 - property g := by - subst hn - obtain ⟨⟨x : C(G, _), hx⟩, hx'⟩ := x - have : (X.ρ g).hom (x (g⁻¹ * 1)) = x 1 := congr(DFunLike.coe (F := C(G, _)) $(hx g) 1) - have hx' : x (g⁻¹ * 1) - x 1 = 0 := - congr(DFunLike.coe (F := C(G, _)) (DFunLike.coe (F := C(G, _)) ($hx').1 1) (g⁻¹ * 1)) - rw [sub_eq_zero] at hx' - exact congr((X.ρ g).hom $hx').symm.trans this } - map_add' _ _ := rfl - map_smul' _ _ := rfl - invFun x := by - refine ⟨⟨ContinuousLinearMap.const R _ x.1, fun g ↦ ContinuousMap.ext fun a ↦ - by subst hn; exact x.2 g⟩, ?_⟩ - subst hn - exact Subtype.ext (ContinuousMap.ext fun a ↦ - ContinuousMap.ext fun b ↦ show x.1 - x.1 = (0 : X.V) by simp) - left_inv x := by - subst hn - obtain ⟨⟨x : C(G, _), hx⟩, hx'⟩ := x - refine Subtype.ext (Subtype.ext <| ContinuousMap.ext fun a ↦ ?_) - have hx' : x 1 - x a = 0 := - congr(DFunLike.coe (F := C(G, _)) (DFunLike.coe (F := C(G, _)) ($hx').1 a) 1) - rwa [sub_eq_zero] at hx' - right_inv _ := rfl - continuous_toFun := continuous_induced_rng.mpr ((continuous_eval_const (F := C(G, _)) 1).comp - (continuous_subtype_val.comp continuous_subtype_val)) - continuous_invFun := continuous_induced_rng.mpr - (continuous_induced_rng.mpr ((ContinuousLinearMap.const R G).cont.comp continuous_subtype_val)) - -set_option backward.isDefEq.respectTransparency false in -open ShortComplex HomologyData in -/-- `H⁰_cont(G, X) ≅ Xᴳ`. -/ -noncomputable -def continuousCohomologyZeroIso : (continuousCohomology R G 0) ≅ invariants R G := - NatIso.ofComponents (fun X ↦ (ofIsLimitKernelFork _ (by simp) _ - (TopModuleCat.isLimitKer _)).left.homologyIso ≪≫ TopModuleCat.ofIso - (kerHomogeneousCochainsZeroEquiv R G X _ (by simp))) fun {X Y} f ↦ by - dsimp [continuousCohomology, HomologicalComplex.homologyMap] - rw [Category.assoc, ← Iso.inv_comp_eq] - rw [LeftHomologyData.leftHomologyIso_inv_naturality_assoc, Iso.inv_hom_id_assoc, - ← cancel_epi (LeftHomologyData.π _), leftHomologyπ_naturality'_assoc] - rfl - -end ContinuousCohomology diff --git a/Mathlib/Algebra/Category/FGModuleCat/Basic.lean b/Mathlib/Algebra/Category/FGModuleCat/Basic.lean index bd7a124b616bc7..eefbde73cc87b9 100644 --- a/Mathlib/Algebra/Category/FGModuleCat/Basic.lean +++ b/Mathlib/Algebra/Category/FGModuleCat/Basic.lean @@ -99,12 +99,6 @@ abbrev of (V : Type v) [AddCommGroup V] [Module R V] [Module.Finite R V] : FGMod lemma of_carrier (V : Type v) [AddCommGroup V] [Module R V] [Module.Finite R V] : of R V = V := rfl -/- -The reduction done by `simpVarHead` is stronger than the one actually used by `simp`, -so we get a false positive here --/ -attribute [nolint simpVarHead] of_carrier - variable {R} in /-- Lift a linear map between finitely generated modules to `FGModuleCat R`. -/ abbrev ofHom {V W : Type v} [AddCommGroup V] [Module R V] [Module.Finite R V] diff --git a/Mathlib/Algebra/Category/Grp/Basic.lean b/Mathlib/Algebra/Category/Grp/Basic.lean index 3f1342fbd31449..58e59d68d21006 100644 --- a/Mathlib/Algebra/Category/Grp/Basic.lean +++ b/Mathlib/Algebra/Category/Grp/Basic.lean @@ -600,19 +600,22 @@ instance CommGrpCat.forget_reflects_isos : (forget CommGrpCat.{u}).ReflectsIsomo -- note: in the following definitions, there is a problem with `@[to_additive]` -- as the `Category` instance is not found on the additive variant -- this variant is then renamed with an `Aux` suffix - +set_option linter.checkUnivs false in /-- An alias for `GrpCat.{max u v}`, to deal around unification issues. -/ -@[to_additive (attr := nolint checkUnivs) GrpMaxAux +@[to_additive GrpMaxAux /-- An alias for `AddGrpCat.{max u v}`, to deal around unification issues. -/] abbrev GrpMax.{u1, u2} := GrpCat.{max u1 u2} + +set_option linter.checkUnivs false in /-- An alias for `AddGrpCat.{max u v}`, to deal around unification issues. -/ -@[nolint checkUnivs] abbrev AddGrpMax.{u1, u2} := AddGrpCat.{max u1 u2} +set_option linter.checkUnivs false in /-- An alias for `CommGrpCat.{max u v}`, to deal around unification issues. -/ -@[to_additive (attr := nolint checkUnivs) AddCommGrpMaxAux +@[to_additive AddCommGrpMaxAux /-- An alias for `AddCommGrpCat.{max u v}`, to deal around unification issues. -/] abbrev CommGrpMax.{u1, u2} := CommGrpCat.{max u1 u2} + +set_option linter.checkUnivs false in /-- An alias for `AddCommGrpCat.{max u v}`, to deal around unification issues. -/ -@[nolint checkUnivs] abbrev AddCommGrpMax.{u1, u2} := AddCommGrpCat.{max u1 u2} diff --git a/Mathlib/Algebra/Category/Grp/EpiMono.lean b/Mathlib/Algebra/Category/Grp/EpiMono.lean index 441946c54d9b03..1414285658ead5 100644 --- a/Mathlib/Algebra/Category/Grp/EpiMono.lean +++ b/Mathlib/Algebra/Category/Grp/EpiMono.lean @@ -13,6 +13,7 @@ public import Mathlib.GroupTheory.QuotientGroup.Defs /-! # Monomorphisms and epimorphisms in `Group` + In this file, we prove monomorphisms in the category of groups are injective homomorphisms and epimorphisms are surjective homomorphisms. -/ diff --git a/Mathlib/Algebra/Category/Grp/LargeColimits.lean b/Mathlib/Algebra/Category/Grp/LargeColimits.lean index 9f169824b42e8d..bc79879e2627e0 100644 --- a/Mathlib/Algebra/Category/Grp/LargeColimits.lean +++ b/Mathlib/Algebra/Category/Grp/LargeColimits.lean @@ -46,7 +46,7 @@ lemma isColimit_iff_bijective_desc [DecidableEq J] : apply ofHom_injective refine hc.hom_ext (fun j ↦ ?_) ext x - rw [ConcreteCategory.comp_apply, ConcreteCategory.comp_apply, ← Quot.ι_desc _ c j x] + erw [ConcreteCategory.comp_apply, ConcreteCategory.comp_apply, ← Quot.ι_desc _ c j x] exact DFunLike.congr_fun eq (Quot.ι F j x) · set c' : Cocone F := { pt := AddCommGrpCat.of (ULift (AddCircle (1 : ℚ))) diff --git a/Mathlib/Algebra/Category/ModuleCat/Basic.lean b/Mathlib/Algebra/Category/ModuleCat/Basic.lean index 4c29f9d53dd76d..752a2035ff5200 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Basic.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Basic.lean @@ -585,6 +585,33 @@ def homMk : M ⟶ N where lemma forget₂_map_homMk : (forget₂ (ModuleCat R) AddCommGrpCat).map (homMk φ hφ) = φ := rfl +/-- Constructor for isomorphisms in `ModuleCat R` taking an isomorphism in `AddCommGrpCat` +and a compatibility condition. -/ +def isoMk (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N) + (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) : + M ≅ N := + LinearEquiv.toModuleIso + { __ := φ.addCommGroupIsoToAddEquiv + map_smul' r x := congr($(hφ r) x).symm } + +@[simp] +lemma isoMk_hom (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N) + (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) : + (isoMk φ hφ).hom = homMk φ.hom hφ := + rfl + +@[simp] +lemma isoMk_inv (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N) + (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) : + (isoMk φ hφ).inv = homMk φ.inv (ModuleCat.smul_naturality (isoMk φ hφ).inv) := + rfl + +@[simp] +lemma isoMk_symm (φ : (forget₂ (ModuleCat R) Ab).obj M ≅ (forget₂ _ _).obj N) + (hφ : ∀ r, φ.hom ≫ N.smul r = M.smul r ≫ φ.hom) : + (isoMk φ hφ).symm = isoMk φ.symm (ModuleCat.smul_naturality (isoMk φ hφ).inv) := + rfl + end instance : (forget (ModuleCat.{v} R)).ReflectsIsomorphisms where @@ -640,3 +667,10 @@ end Bilinear @[simp] theorem LinearMap.id_moduleCat_comp {R} [Ring R] {G : Type u} [AddCommGroup G] [Module R G] {H : ModuleCat.{u} R} (f : G →ₗ[R] H) : LinearMap.comp (𝟙 H : H ⟶ H).hom f = f := by simp + +instance {R S : Type*} [Ring R] [Ring S] (F : ModuleCat R ⥤ ModuleCat S) [F.Full] [F.Faithful] + (M : ModuleCat R) [h : Nontrivial M] : Nontrivial (F.obj M) := by + by_contra! + exact ((not_iff_not.2 ModuleCat.isZero_iff_subsingleton).2 <| + not_subsingleton_iff_nontrivial.2 h) <| IsZero.of_full_of_faithful_of_isZero F _ <| + ModuleCat.isZero_of_subsingleton <| F.obj M diff --git a/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean b/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean index 0f4781fb157094..b162815dd0a236 100644 --- a/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean +++ b/Mathlib/Algebra/Category/ModuleCat/ChangeOfRings.lean @@ -87,6 +87,17 @@ def restrictScalars {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R → obj := RestrictScalars.obj' f map := RestrictScalars.map' f +@[simp] +lemma smul_restrictScalars {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R →+* S) (r : R) + (M : ModuleCat S) : + dsimp% ((ModuleCat.restrictScalars f).obj M).smul r = M.smul (f r) := + rfl + +lemma forget₂_map_restrictScalars {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R →+* S) + {M N : ModuleCat S} (g : M ⟶ N) : + (forget₂ _ Ab).map ((ModuleCat.restrictScalars f).map g) = (forget₂ _ Ab).map g := + rfl + instance {R : Type u₁} {S : Type u₂} [Ring R] [Ring S] (f : R →+* S) : (restrictScalars.{v} f).Faithful where map_injective h := by diff --git a/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean b/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean index 1ba7a0e47b858a..0f8b07457d348c 100644 --- a/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean +++ b/Mathlib/Algebra/Category/ModuleCat/FilteredColimits.lean @@ -175,9 +175,10 @@ def colimitDesc (t : Cocone F) : colimit F ⟶ t.pt := obtain ⟨j, x, rfl⟩ := M.mk_surjective F x simp [hf] } +set_option backward.defeqAttrib.useBackward true in @[reassoc (attr := simp)] lemma ι_colimitDesc (t : Cocone F) (j : J) : - (colimitCocone F).ι.app j ≫ colimitDesc F t = t.ι.app j := + dsimp% (colimitCocone F).ι.app j ≫ colimitDesc F t = t.ι.app j := (forget₂ _ AddCommGrpCat).map_injective ((AddCommGrpCat.FilteredColimits.colimitCoconeIsColimit (F ⋙ forget₂ _ _)).fac _ _) diff --git a/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean b/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean index b2d94c8c704d37..f0044570af4178 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Presheaf.lean @@ -77,6 +77,19 @@ lemma map_comp_apply {U V W : Cᵒᵖ} (i : U ⟶ V) (j : V ⟶ W) (x) : M.map (i ≫ j) x = M.map j (M.map i x) := by rw [M.map_comp]; rfl +set_option backward.isDefEq.respectTransparency false in +/-- The restriction map `M.map f` of a presheaf of modules `M`, bundled as a semilinear map +along the ring map `R.map f`. -/ +noncomputable def restrictₛₗ {X Y : Cᵒᵖ} (f : X ⟶ Y) : + M.obj X →ₛₗ[(R.map f).hom] M.obj Y where + toFun m := M.map f m + map_add' := map_add (M.map f).hom + map_smul' r m := M.map_smul f r m + +@[simp] +lemma restrictₛₗ_apply {X Y : Cᵒᵖ} (f : X ⟶ Y) (m : M.obj X) : + M.restrictₛₗ f m = M.map f m := rfl + /-- A morphism of presheaves of modules consists of a family of linear maps which satisfy the naturality condition. -/ @[ext] @@ -140,6 +153,13 @@ lemma presheaf_obj_coe (X : Cᵒᵖ) : lemma presheaf_map_apply_coe {X Y : Cᵒᵖ} (f : X ⟶ Y) (x : M.obj X) : DFunLike.coe (α := M.obj X) (β := fun _ ↦ M.obj Y) (M.presheaf.map f).hom x = M.map f x := rfl +@[reassoc] +lemma smul_map {U V : Cᵒᵖ} (f : U ⟶ V) (r : R.obj U) : + dsimp% ModuleCat.smul _ r ≫ M.presheaf.map f = + M.presheaf.map f ≫ ModuleCat.smul _ (R.map f r) := by + ext x + exact (M.map f).hom.map_smul r x + instance (M : PresheafOfModules R) (X : Cᵒᵖ) : Module (R.obj X) (M.presheaf.obj X) := inferInstanceAs (Module (R.obj X) (M.obj X)) diff --git a/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean b/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean index db8c9a6831aa72..1b537629bacb2a 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Presheaf/Pushforward.lean @@ -89,6 +89,15 @@ a morphism of presheaves of rings `S ⟶ F.op ⋙ R`. -/ noncomputable def pushforward : PresheafOfModules.{v} R ⥤ PresheafOfModules.{v} S := pushforward₀ F R ⋙ restrictScalars φ +lemma forget₂_map_pushforward_obj_map {U V : Cᵒᵖ} (f : U ⟶ V) (M : PresheafOfModules R) : + (forget₂ _ Ab).map (((PresheafOfModules.pushforward φ).obj M).map f) = + M.presheaf.map (F.map f.unop).op := + rfl + +lemma forget₂_map_pushforward_map_app {U : Cᵒᵖ} {M N : PresheafOfModules _} (g : M ⟶ N) : + (forget₂ _ Ab).map (((pushforward φ).map g).app U) = (forget₂ _ Ab).map (g.app _) := + rfl + /-- The pushforward of presheaves of modules commutes with the forgetful functor to presheaves of abelian groups. -/ noncomputable def pushforwardCompToPresheaf : diff --git a/Mathlib/Algebra/Category/ModuleCat/Presheaf/Submodule.lean b/Mathlib/Algebra/Category/ModuleCat/Presheaf/Submodule.lean new file mode 100644 index 00000000000000..8becc6ba619651 --- /dev/null +++ b/Mathlib/Algebra/Category/ModuleCat/Presheaf/Submodule.lean @@ -0,0 +1,140 @@ +/- +Copyright (c) 2026 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Merten +-/ +module + +public import Mathlib.Algebra.Category.ModuleCat.Presheaf.EpiMono + +/-! +# Submodules of presheaves of modules + +Given a presheaf of modules `M` over a presheaf of rings `R` and a family of +submodules `N X` of `M.obj X` that is stable under the restriction maps of `M`, +we construct the corresponding subobject of `M` in the category +`PresheafOfModules R`. + +## Main definitions + +- `PresheafOfModules.Submodule M`: a family of submodules of `M`, stable + under restriction. +- `PresheafOfModules.Submodule.toPresheafOfModules`: the associated + presheaf of modules. + +The families of submodules of `M` form a `CompleteLattice`, with all the lattice +operations computed pointwise. +-/ + +@[expose] public section + +universe v v₁ u₁ u + +open CategoryTheory + +namespace PresheafOfModules + +variable {C : Type u₁} [Category.{v₁} C] {R : Cᵒᵖ ⥤ RingCat.{u}} + +/-- A family of submodules `N X` of `M.obj X`, for a presheaf of modules `M`, stable +under the restriction maps of `M`. This defines a subobject of `M` in `PresheafOfModules R`. -/ +structure Submodule (M : PresheafOfModules.{v} R) where + /-- the submodule of `M.obj X` -/ + obj (X : Cᵒᵖ) : _root_.Submodule (R.obj X) (M.obj X) + /-- the family is stable under restriction -/ + map {X Y : Cᵒᵖ} (f : X ⟶ Y) : obj X ≤ (obj Y).comap (M.restrictₛₗ f) + +namespace Submodule + +variable {M : PresheafOfModules.{v} R} (N : M.Submodule) + +@[ext] +lemma ext {N₁ N₂ : M.Submodule} (h : ∀ X, N₁.obj X = N₂.obj X) : + N₁ = N₂ := by + cases N₁; cases N₂; congr 1; ext X : 1; exact h X + +@[grind .] +lemma map_mem {X Y : Cᵒᵖ} (f : X ⟶ Y) {x : M.obj X} (hx : x ∈ N.obj X) : + M.map f x ∈ N.obj Y := + N.map f hx + +attribute [local simp] LinearMap.restrict_apply ModuleCat.semilinearMapAddEquiv in +set_option backward.isDefEq.respectTransparency false in +/-- The presheaf of modules associated to a submodule. -/ +@[simps! obj] +noncomputable def toPresheafOfModules : PresheafOfModules.{v} R where + obj X := ModuleCat.of (R.obj X) (N.obj X) + map {X Y} f := + ModuleCat.semilinearMapAddEquiv _ _ _ <| + (M.restrictₛₗ f).restrict (p := N.obj X) (q := N.obj Y) (fun _ hc ↦ N.map_mem _ hc) + +@[simp] +lemma toPresheafOfModules_map_apply {X Y : Cᵒᵖ} (f : X ⟶ Y) (m : N.obj X) : + dsimp% ((N.toPresheafOfModules).map f m).val = M.map f m.val := by + rfl + +/-- The inclusion of a submodule into the ambient presheaf of modules. -/ +@[simps!] +noncomputable def ι : N.toPresheafOfModules ⟶ M := + homMk { app X := AddCommGrpCat.ofHom (N.obj X).subtype.toAddMonoidHom } (by cat_disch) + +instance : Mono N.ι := mono_of_injective fun _ ↦ Subtype.val_injective + +instance : PartialOrder M.Submodule := + PartialOrder.lift _ fun _ _ h ↦ ext (congrFun h) + +lemma le_iff {N₁ N₂ : M.Submodule} : N₁ ≤ N₂ ↔ ∀ X, N₁.obj X ≤ N₂.obj X := + .rfl + +/-- If `N₁` and `N₂` are submodule with `N₁ ≤ N₂`, this is the associated inclusion +of presheaves of modules. -/ +@[simps!] +noncomputable def homOfLE {N₁ N₂ : M.Submodule} (hle : N₁ ≤ N₂) : + N₁.toPresheafOfModules ⟶ N₂.toPresheafOfModules := + homMk { app X := AddCommGrpCat.ofHom (Submodule.inclusion (hle X)).toAddMonoidHom } (by cat_disch) + +instance (N₁ N₂ : M.Submodule) (hle : N₁ ≤ N₂) : Mono (homOfLE hle) := + mono_of_injective fun _ ↦ Submodule.inclusion_injective (hle _) + +@[reassoc (attr := simp)] +lemma homOfLE_ι {N₁ N₂ : M.Submodule} (hle : N₁ ≤ N₂) : homOfLE hle ≫ N₂.ι = N₁.ι := rfl + +@[simps sup_obj inf_obj sSup_obj sInf_obj top_obj bot_obj] +instance : CompleteLattice M.Submodule where + sup F G := + { obj X := F.obj X ⊔ G.obj X + map f := sup_le ((F.map f).trans (Submodule.comap_mono le_sup_left)) + ((G.map f).trans (Submodule.comap_mono le_sup_right)) } + le_sup_left _ _ _ := le_sup_left + le_sup_right _ _ _ := le_sup_right + sup_le _ _ _ h₁ h₂ X := sup_le (h₁ X) (h₂ X) + inf F G := + { obj X := F.obj X ⊓ G.obj X + map f := le_inf (inf_le_left.trans (F.map f)) (inf_le_right.trans (G.map f)) } + inf_le_left _ _ _ := inf_le_left + inf_le_right _ _ _ := inf_le_right + le_inf _ _ _ h₁ h₂ X := le_inf (h₁ X) (h₂ X) + sSup s := + { obj X := ⨆ N ∈ s, N.obj X + map f := iSup₂_le fun N hN ↦ (N.map f).trans + (Submodule.comap_mono (le_iSup₂_of_le N hN le_rfl)) } + isLUB_sSup _ := + ⟨fun N hN _ ↦ le_iSup₂_of_le N hN le_rfl, fun _ hb X ↦ iSup₂_le fun _ hN ↦ hb hN X⟩ + sInf s := + { obj X := ⨅ N ∈ s, N.obj X + map f := by + simp_rw [Submodule.comap_iInf, le_iInf₂_iff] + intro N hN + refine iInf₂_le_of_le _ hN (N.map _) } + isGLB_sInf _ := + ⟨fun N hN _ ↦ iInf₂_le N hN, fun _ hb X ↦ le_iInf₂ fun _ hN ↦ hb hN X⟩ + bot.obj := ⊥ + bot.map _ := bot_le + bot_le _ _ := bot_le + top.obj := ⊤ + top.map _ := le_top + le_top _ _ := le_top + +end Submodule + +end PresheafOfModules diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf.lean index 68e4a7101d9812..e61222033fe2d3 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf.lean @@ -5,6 +5,7 @@ Authors: Joël Riou -/ module +public import Mathlib.Algebra.Category.AlgCat.TensorAlgebra public import Mathlib.Algebra.Category.ModuleCat.Presheaf public import Mathlib.Algebra.Category.ModuleCat.Limits public import Mathlib.CategoryTheory.Sites.LocallyBijective @@ -166,8 +167,6 @@ def sectionsFunctor : SheafOfModules.{v} R ⥤ Type _ where obj M := M.sections map f := ↾(sectionsMap f) -variable [J.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - variable (R) in /-- The obvious free sheaf of modules of rank `1`. -/ @[simps] diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Free.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Free.lean index b19bc3ec15312c..4f00bd861e4819 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Free.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Free.lean @@ -32,7 +32,6 @@ open CategoryTheory Limits variable {C : Type u₁} [Category.{v₁} C] {J : GrothendieckTopology C} {R : Sheaf J RingCat.{u}} [HasWeakSheafify J AddCommGrpCat.{u}] [J.WEqualsLocallyBijective AddCommGrpCat.{u}] - [J.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] namespace SheafOfModules @@ -175,7 +174,6 @@ section variable {C' : Type u₂} [Category.{v₂} C'] {J' : GrothendieckTopology C'} {S : Sheaf J' RingCat.{u}} [HasSheafify J' AddCommGrpCat.{u}] [J'.WEqualsLocallyBijective AddCommGrpCat.{u}] - [J'.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] (F : SheafOfModules.{u} R ⥤ SheafOfModules.{u} S) (I : Type u) /-- Let `F` be a functor from the category of sheaves of `R`-modules to sheaves of `S`-modules. diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Generators.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Generators.lean index 4c736dd68e9cc4..6b6e2d92053be6 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Generators.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Generators.lean @@ -35,7 +35,6 @@ open CategoryTheory Limits variable {C : Type u'} [Category.{v'} C] {J : GrothendieckTopology C} {R : Sheaf J RingCat.{u}} [HasWeakSheafify J AddCommGrpCat.{u}] [J.WEqualsLocallyBijective AddCommGrpCat.{u}] - [J.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] namespace SheafOfModules @@ -108,7 +107,6 @@ section variable [∀ (X : C), HasWeakSheafify (J.over X) AddCommGrpCat.{u}] [∀ (X : C), (J.over X).WEqualsLocallyBijective AddCommGrpCat.{u}] - [∀ (X : C), (J.over X).HasSheafCompose (forget₂ RingCat AddCommGrpCat.{u})] /-- The data of generating sections of the restriction of a sheaf of modules over a covering of the terminal object. -/ @@ -151,7 +149,6 @@ noncomputable section variable {C' : Type u₁} [Category.{v₁} C'] {J' : GrothendieckTopology C'} {S : Sheaf J' RingCat.{u}} [HasSheafify J' AddCommGrpCat] [J'.WEqualsLocallyBijective AddCommGrpCat] - [J'.HasSheafCompose (forget₂ RingCat AddCommGrpCat)] variable {M : SheafOfModules.{u} R} (G : M.GeneratingSections) (F : SheafOfModules.{u} R ⥤ SheafOfModules.{u} S) [PreservesColimitsOfSize.{u, u} F] @@ -189,8 +186,7 @@ instance [IsIso G.π] : IsIso (G.map F η).π := by rw [GeneratingSections.map_π_eq] infer_instance -variable [∀ X, (J.over X).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] [HasBinaryProducts C] +variable [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] [HasBinaryProducts C] [∀ X, (J.over X).WEqualsLocallyBijective AddCommGrpCat.{u}] /-- Given `G : M.GeneratingSections`, we naturally obtain `M.LocalGeneratorsData` using the diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/LocallyFree.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/LocallyFree.lean index 8a538d40092774..c157a2ada79219 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/LocallyFree.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/LocallyFree.lean @@ -37,8 +37,7 @@ namespace SheafOfModules section -variable [∀ X, (J.over X).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [∀ X, HasWeakSheafify (J.over X) AddCommGrpCat.{u}] +variable [∀ X, HasWeakSheafify (J.over X) AddCommGrpCat.{u}] [∀ X, (J.over X).WEqualsLocallyBijective AddCommGrpCat.{u}] namespace LocalGeneratorsData @@ -70,7 +69,6 @@ end section variable [HasWeakSheafify J AddCommGrpCat.{u}] [J.WEqualsLocallyBijective AddCommGrpCat.{u}] - [J.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] /-- The generating sections of the free sheaf of modules. -/ @[expose, simps] @@ -91,8 +89,7 @@ instance (I : Type u) : IsIso (free.generatingSections (R := R) I).π := by rw [free.generatingSections_π] infer_instance -variable [∀ X, (J.over X).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] [HasBinaryProducts C] +variable [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] [HasBinaryProducts C] [∀ X, (J.over X).WEqualsLocallyBijective AddCommGrpCat.{u}] [HasSheafify J AddCommGrpCat] set_option backward.defeqAttrib.useBackward true in @@ -110,8 +107,7 @@ end section -variable [∀ X, (J.over X).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] +variable [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] [∀ X, (J.over X).WEqualsLocallyBijective AddCommGrpCat.{u}] namespace LocalGeneratorsData diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PullbackFree.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PullbackFree.lean index 763ff98768043c..abdf63e2098e0f 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PullbackFree.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PullbackFree.lean @@ -55,9 +55,6 @@ lemma bijective_pushforwardSections [F.Final] : Function.Bijective (pushforwardSections φ (M := M)) := Functor.bijective_sectionsPrecomp _ _ -variable [J.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [K.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - /-- The canonical morphism `unit S ⟶ (pushforward.{u} φ).obj (unit R)` of sheaves of modules corresponding to a continuous map between ringed sites. -/ noncomputable def unitToPushforwardObjUnit : unit S ⟶ (pushforward.{u} φ).obj (unit R) where diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean index 3686a18ae2f518..e0d9552472687d 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/PushforwardContinuous.lean @@ -48,6 +48,11 @@ noncomputable def pushforward : SheafOfModules.{v} R ⥤ SheafOfModules.{v} S wh map f := { val := (PresheafOfModules.pushforward φ.hom).map f.val } +lemma forget₂_map_pushforward_obj_val_map {U V : Cᵒᵖ} (f : U ⟶ V) (M) : + (forget₂ _ Ab).map (((pushforward.{v} φ).obj M).val.map f) = + M.val.presheaf.map (F.map f.unop).op := + rfl + variable (R) in /-- The restriction functor from sheaves of `R`-modules to sheaves of `R.over X`-modules for some `X : D`. -/ @@ -83,9 +88,7 @@ noncomputable def overFunctorMap {X Y : D} (f : X ⟶ Y) : /-- The pushforward of `R.over Y` along `Over.map f` is isomorphic to `R.over X`. -/ @[simps! +dsimpLhs] -noncomputable def overMapUnitIso {X Y : D} - [(K.over X).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [(K.over Y).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] (f : X ⟶ Y) : +noncomputable def overMapUnitIso {X Y : D} (f : X ⟶ Y) : (overMap.{u} R f).obj (.unit (R.over Y)) ≅ .unit (R.over X) := Iso.refl _ @@ -128,12 +131,13 @@ section variable {K' : GrothendieckTopology D'} {K'' : GrothendieckTopology D''} {G : D ⥤ D'} {R' : Sheaf K' RingCat.{u}} - [Functor.IsContinuous G K K'] [Functor.IsContinuous (F ⋙ G) J K'] + [Functor.IsContinuous G K K'] (ψ : R ⟶ (G.sheafPushforwardContinuous RingCat.{u} K K').obj R') /-- The composition of two pushforward functors on categories of sheaves of modules identify to the pushforward for the composition. -/ noncomputable def pushforwardComp : + haveI : Functor.IsContinuous (F ⋙ G) J K' := Functor.isContinuous_comp _ _ _ K _ pushforward.{v} ψ ⋙ pushforward.{v} φ ≅ pushforward.{v} (F := F ⋙ G) (φ ≫ (F.sheafPushforwardContinuous RingCat.{u} J K).map ψ) := Iso.refl _ @@ -149,8 +153,7 @@ lemma pushforwardComp_inv_app_val_app (M U x) : variable {G' : D' ⥤ D''} {R'' : Sheaf K'' RingCat.{u}} [Functor.IsContinuous G' K' K''] [Functor.IsContinuous (G ⋙ G') K K''] - [Functor.IsContinuous ((F ⋙ G) ⋙ G') J K''] - [Functor.IsContinuous (F ⋙ G ⋙ G') J K''] + [(F ⋙ G).IsContinuous J K'] (ψ' : R' ⟶ (G'.sheafPushforwardContinuous RingCat.{u} K' K'').obj R'') lemma pushforward_assoc : @@ -240,6 +243,14 @@ noncomputable def pushforwardNatIso (α : F ≅ G) : simp only [← Functor.map_comp, ← op_comp, Iso.hom_inv_id_app, op_id, CategoryTheory.Functor.map_id] +/-- More flexible variant of `SheafOfModules.pushforwardNatIso`. -/ +@[simps!] +noncomputable +def pushforwardCongr₂ {ψ : T ⟶ (F.sheafPushforwardContinuous RingCat J K).obj S} (e : F ≅ G) + (he : φ ≫ (Functor.sheafPushforwardContinuousNatTrans e.hom _ _ _).app S = ψ) : + pushforward.{v} φ ≅ pushforward.{v} ψ := + pushforwardNatIso _ e ≪≫ pushforwardCongr he + end NatTrans section Adjunction @@ -292,6 +303,23 @@ lemma pushforwardPushforwardAdj_counit_app_val_app (M U x) : ((pushforwardPushforwardAdj adj φ ψ H₁ H₂).counit.app M).val.app U x = M.val.map (adj.unit.app U.unop).op x := rfl +set_option backward.defeqAttrib.useBackward true in +instance isLeftAdjoint_pushforward_of_isIso [F.IsCocontinuous J K] [IsIso φ] [F.IsLeftAdjoint] : + (pushforward.{u} φ).IsLeftAdjoint := by + let adj := Adjunction.ofIsLeftAdjoint F + let shAdj := adj.sheafPushforwardContinuous (E := RingCat.{u}) J K + let ψ : R ⟶ (F.rightAdjoint.sheafPushforwardContinuous RingCat.{u} K J).obj S := + shAdj.unit.app R ≫ (F.rightAdjoint.sheafPushforwardContinuous _ _ _).map (inv φ) + refine (SheafOfModules.pushforwardPushforwardAdj adj φ ψ ?_ ?_).isLeftAdjoint + · ext U : 2 + simp [ψ, shAdj] + · ext U : 2 + have := (inv φ).hom.naturality + dsimp at this + simp only [ObjectProperty.hom_inv, NatIso.isIso_inv_app, sheafPushforwardContinuous_obj_obj_obj, + IsIso.eq_inv_comp] at this + simp [ψ, shAdj, ← this, ← Functor.map_comp_assoc, ← op_comp] + noncomputable section open CategoryTheory Limits diff --git a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean index 0356d546d27ba4..cecd0a5950edee 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Sheaf/Quasicoherent.lean @@ -36,7 +36,6 @@ namespace SheafOfModules section variable [HasWeakSheafify J AddCommGrpCat.{u}] [J.WEqualsLocallyBijective AddCommGrpCat.{u}] - [J.HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] /-- A global presentation of a sheaf of modules `M` consists of a family `generators.s` of sections `s` which generate `M`, and a family of sections which generate @@ -66,7 +65,7 @@ noncomputable section variable {C : Type u₁} [Category.{v₁} C] {J : GrothendieckTopology C} {R : Sheaf J RingCat.{u}} [HasSheafify J AddCommGrpCat] [J.WEqualsLocallyBijective AddCommGrpCat] - [J.HasSheafCompose (forget₂ RingCat AddCommGrpCat)] {ι σ : Type u} + {ι σ : Type u} /-- Given two morphisms of sheaves of `R`-modules `f : free ι ⟶ free σ` and `g : free σ ⟶ M` satisfying `H : f ≫ g = 0` and `IsColimit (CokernelCofork.ofπ g H)`, we obtain @@ -144,7 +143,6 @@ instance {M N : SheafOfModules.{u} R} (f : M ⟶ N) [IsIso f] variable {C' : Type u₂} [Category.{v₂} C'] {J' : GrothendieckTopology C'} {S : Sheaf J' RingCat.{u}} [HasSheafify J' AddCommGrpCat] [J'.WEqualsLocallyBijective AddCommGrpCat] - [J'.HasSheafCompose (forget₂ RingCat AddCommGrpCat)] variable {M : SheafOfModules.{u} R} (P : Presentation M) (F : SheafOfModules.{u} R ⥤ SheafOfModules.{u} S) [PreservesColimitsOfSize.{u, u} F] @@ -194,8 +192,7 @@ end section -variable [∀ X, (J.over X).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [∀ X, HasWeakSheafify (J.over X) AddCommGrpCat.{u}] +variable [∀ X, HasWeakSheafify (J.over X) AddCommGrpCat.{u}] [∀ X, (J.over X).WEqualsLocallyBijective AddCommGrpCat.{u}] /-- This structure contains the data of a family of objects `X i` which cover @@ -260,6 +257,9 @@ variable (R) in abbrev isQuasicoherent : ObjectProperty (SheafOfModules.{u} R) := IsQuasicoherent +instance (M : (isQuasicoherent R).FullSubcategory) : M.obj.IsQuasicoherent := + M.property + /-- A sheaf of modules is finitely presented if it is locally the cokernel of a morphism between coproducts of finitely many copies of the sheaf of rings. -/ class IsFinitePresentation (M : SheafOfModules.{u} R) : Prop where @@ -282,6 +282,78 @@ instance (M : SheafOfModules.{u} R) [M.IsFinitePresentation] : obtain ⟨σ, _⟩ := IsFinitePresentation.exists_quasicoherentData M exact ⟨σ.localGeneratorsData, inferInstance⟩ +section map + +variable {D : Type u₂} [Category.{v₂, u₂} D] {K : GrothendieckTopology D} + {S : Sheaf K RingCat.{u}} [∀ (X : D), (K.over X).WEqualsLocallyBijective AddCommGrpCat] + +variable [∀ (X : C), HasSheafify (J.over X) AddCommGrpCat.{u}] + [∀ (X : D), HasSheafify (K.over X) AddCommGrpCat.{u}] + +variable (G : D ⥤ C) [G.IsContinuous K J] [G.IsCocontinuous K J] + (φ : S ⟶ (G.sheafPushforwardContinuous RingCat.{u} K J).obj R) + +/-- The pushforward of `SheafOfModules.QuasicoherentData` along a continuous +and cocontinuous functor. -/ +-- TODO: Remove the continuous assumption on `Over.post` here and below. +@[simps I X] +noncomputable def QuasicoherentData.pushforward (η : (pushforward φ).obj (unit R) ≅ unit S) + [∀ (X : D), (Over.post G).IsContinuous (K.over X) (J.over _)] + (h : ∀ (X : D) (Y : C) (f : G.obj X ⟶ Y), + PreservesColimitsOfSize.{u, u} <| + pushforward.{u} (R := (R.over Y)) (F := Over.post (X := X) G ⋙ Over.map f) + (((Over.forget X).sheafPushforwardContinuous RingCat.{u} (K.over X) K).map φ)) + {M : SheafOfModules.{u} R} (P : M.QuasicoherentData) : + QuasicoherentData ((pushforward φ).obj M) where + I := Σ (X : D) (i : P.I), G.obj X ⟶ P.X i + X i := i.1 + coversTop Y := by + refine K.superset_covering ?_ <| G.cover_lift K _ (P.coversTop (G.obj Y)) + intro Z g ⟨i, ⟨v⟩⟩ + exact ⟨⟨Z, i, v⟩, ⟨𝟙 _⟩⟩ + presentation i := by + letI overS : SheafOfModules.{u} S ⥤ SheafOfModules.{u} (S.over i.1) := + SheafOfModules.pushforward (𝟙 _) + letI G' := Over.post (X := i.1) G ⋙ Over.map i.2.2 + letI ψ : S.over i.1 ⟶ + (G'.sheafPushforwardContinuous RingCat.{u} (K.over i.1) (J.over (P.X i.2.1))).obj + (R.over (P.X i.2.1)) := + ((Over.forget i.1).sheafPushforwardContinuous RingCat.{u} (K.over i.1) K).map φ + letI e : (SheafOfModules.pushforward ψ).obj (unit (R.over (P.X i.snd.fst))) ≅ + unit (S.over i.fst) := overS.mapIso η + haveI : PreservesColimitsOfSize.{u, u, _} (SheafOfModules.pushforward ψ) := h _ _ _ + exact (P.presentation i.2.1).map (SheafOfModules.pushforward ψ) e.symm + +lemma isQuasicoherent_pushforward (η : (pushforward φ).obj (unit R) ≅ unit S) + [∀ (X : D), (Over.post G).IsContinuous (K.over X) (J.over _)] + (h : ∀ (X : D) (Y : C) (f : G.obj X ⟶ Y), + PreservesColimitsOfSize.{u, u} <| + pushforward.{u} (R := (R.over Y)) (F := Over.post (X := X) G ⋙ Over.map f) + (((Over.forget X).sheafPushforwardContinuous RingCat.{u} (K.over X) K).map φ)) + {M : SheafOfModules.{u} R} [IsQuasicoherent M] : + IsQuasicoherent ((pushforward φ).obj M) := + IsQuasicoherent.nonempty_quasicoherentData.some.pushforward G φ η h |>.isQuasicoherent + +set_option backward.isDefEq.respectTransparency false in +lemma isQuasicoherent_pushforward_of_isLeftAdjoint (η : (pushforward φ).obj (unit R) ≅ unit S) + [G.IsLeftAdjoint] [IsIso φ] + [∀ X, Functor.IsContinuous (Over.post (X := X) G) (K.over _) (J.over _)] + [HasPullbacks C] [HasPullbacks D] + {M : SheafOfModules.{u} R} [IsQuasicoherent M] : + IsQuasicoherent ((pushforward φ).obj M) := by + apply +allowSynthFailures isQuasicoherent_pushforward G φ η _ + intro X Y f + let G' := Over.post (X := X) G ⋙ Over.map f + have : G'.IsContinuous (K.over X) (J.over Y) := Functor.isContinuous_comp _ _ _ (J.over _) _ + have : G'.IsCocontinuous (K.over X) (J.over Y) := isCocontinuous_comp _ _ _ (J.over _) + let a : S.over X ⟶ + (G'.sheafPushforwardContinuous RingCat.{u} (K.over X) (J.over Y)).obj (R.over Y) := + ((Over.forget X).sheafPushforwardContinuous RingCat.{u} (K.over X) K).map φ + have : (pushforward.{u} a).IsLeftAdjoint := isLeftAdjoint_pushforward_of_isIso a + infer_instance + +end map + end noncomputable section @@ -290,10 +362,8 @@ open CategoryTheory Limits variable {C : Type u₁} [Category.{v₁} C] [HasBinaryProducts C] {J : GrothendieckTopology C} {R : Sheaf J RingCat.{u}} [HasSheafify J AddCommGrpCat] [J.WEqualsLocallyBijective AddCommGrpCat] - [J.HasSheafCompose (forget₂ RingCat AddCommGrpCat)] -variable [∀ X, (J.over X).HasSheafCompose (forget₂ RingCat AddCommGrpCat)] - [∀ X, HasSheafify (J.over X) AddCommGrpCat] +variable [∀ X, HasSheafify (J.over X) AddCommGrpCat] [∀ X, (J.over X).WEqualsLocallyBijective AddCommGrpCat] /-- Given a sheaf of `R`-modules `M` and a `Presentation M`, we may construct the quasi-coherent @@ -344,10 +414,8 @@ end section bind -variable [∀ X, (J.over X).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] - [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] +variable [∀ X, HasSheafify (J.over X) AddCommGrpCat.{u}] [∀ X, (J.over X).WEqualsLocallyBijective AddCommGrpCat.{u}] - [∀ X Y, ((J.over X).over Y).HasSheafCompose (forget₂ RingCat.{u} AddCommGrpCat.{u})] [∀ X Y, HasSheafify ((J.over X).over Y) AddCommGrpCat.{u}] [∀ X Y, ((J.over X).over Y).WEqualsLocallyBijective AddCommGrpCat.{u}] @@ -375,6 +443,12 @@ lemma IsQuasicoherent.of_coversTop {R : Sheaf J RingCat.{u}} (QuasicoherentData.bind M X hX fun _ ↦ IsQuasicoherent.nonempty_quasicoherentData.some).isQuasicoherent +set_option backward.isDefEq.respectTransparency false in +lemma isQuasicoherent_over + [HasPullbacks C] [HasBinaryProducts C] (M : SheafOfModules.{u} R) (X : C) [IsQuasicoherent M] : + IsQuasicoherent (M.over X) := + isQuasicoherent_pushforward_of_isLeftAdjoint _ _ (Iso.refl _) + end bind end SheafOfModules diff --git a/Mathlib/Algebra/Category/ModuleCat/Stalk.lean b/Mathlib/Algebra/Category/ModuleCat/Stalk.lean index de9b7f189934c7..96073610f7a15f 100644 --- a/Mathlib/Algebra/Category/ModuleCat/Stalk.lean +++ b/Mathlib/Algebra/Category/ModuleCat/Stalk.lean @@ -12,8 +12,8 @@ public import Mathlib.CategoryTheory.Limits.Filtered public import Mathlib.Topology.Sheaves.Stalks /-! - # Module structure on stalks + Let `M` be a presheaf of `R`-modules on a topological space. We endow `M.presheaf.stalk x` with an `R.stalk x`-module structure. diff --git a/Mathlib/Algebra/Category/Ring/Constructions.lean b/Mathlib/Algebra/Category/Ring/Constructions.lean index cf49ecd37cfa4a..ff793325838353 100644 --- a/Mathlib/Algebra/Category/Ring/Constructions.lean +++ b/Mathlib/Algebra/Category/Ring/Constructions.lean @@ -5,13 +5,17 @@ Authors: Andrew Yang -/ module -public import Mathlib.Algebra.Category.Ring.Colimits +public import Mathlib.Algebra.Category.Ring.Adjunctions public import Mathlib.Algebra.Category.Ring.Instances public import Mathlib.Algebra.Category.Ring.Limits public import Mathlib.CategoryTheory.Limits.Shapes.StrictInitial public import Mathlib.RingTheory.Localization.BaseChange public import Mathlib.CategoryTheory.Limits.Shapes.Pullback.IsPullback.Basic +import Mathlib.RingTheory.FreeCommRing +import Mathlib.Algebra.Ring.Subring.Units +import Mathlib.CategoryTheory.Adjunction.Limits + /-! # Constructions of (co)limits in `CommRingCat` @@ -335,6 +339,53 @@ noncomputable def _root_.RingEquiv.piEquivPi (R : ι → Type u) [∀ i, CommRin end Pi +namespace Limits + +variable {J : Type u'} [SmallCategory J] (F : J ⥤ CommRingCat.{u}) {c : Cone F} + +set_option backward.isDefEq.respectTransparency false in +theorem isUnit_iff_forall_isUnit (hc : IsLimit c) (r : c.pt) : IsUnit r ↔ + ∀ (j : J), IsUnit (c.π.app j r) := by + refine ⟨fun h _ ↦ h.map _, fun h ↦ ?_⟩ + simp only [isUnit_iff_exists_inv] at h ⊢ + choose inv h_inv using h + have map_inv {j k : J} (f : j ⟶ k) : F.map f (inv j) = inv k := by + have h := congr(F.map f $(h_inv j)) + have : F.map f (c.π.app j r) = c.π.app k r := + DFunLike.congr_fun (congr(Hom.hom $(c.w f))) r + rw [map_mul, map_one, this] at h + rw [← mul_one (F.map f (inv j)), ← h_inv k, ← mul_assoc] + nth_rw 2 [mul_comm]; rw [h, one_mul] + let inv_r : Cone F := .mk (CommRingCat.of (FreeCommRing PUnit)) { + app j := ConcreteCategory.ofHom (FreeCommRing.lift (fun _ ↦ inv j)) + naturality j k f := by + ext1; change FreeCommRing.lift (fun _ => inv k) = _ + ext; simp [map_inv f] } + use hc.lift inv_r (FreeCommRing.of PUnit.unit) + refine Concrete.isLimit_ext _ hc _ _ fun j ↦ ?_ + rw [RingHom.map_mul, RingHom.map_one]; convert h_inv j + change (hc.lift inv_r ≫ c.π.app j) (FreeCommRing.of PUnit.unit) = inv j + rw [IsLimit.fac]; exact FreeCommRing.lift_of .. + +-- The assumption `hj` can be generalized to a zigzag-like assumption of finite steps. +theorem π_isLocalHom (hc : IsLimit c) (j : J) (hj : ∀ (x : c.pt), IsUnit (c.π.app j x) → + ∀ (i : J), ∃ (k : J) (f : i ⟶ k) (g : j ⟶ k), IsLocalHom (F.map f).hom ∧ + F.map f (c.π.app i x) = F.map g (c.π.app j x)) : + IsLocalHom (c.π.app j).hom := by + refine ⟨fun (x : c.pt) hx ↦ (?_ : IsUnit x)⟩ + rw [isUnit_iff_forall_isUnit F hc]; intro i + obtain ⟨k, f, g, lh, eq⟩ := hj x hx i + exact lh.map_nonunit _ (eq ▸ hx.map _) + +theorem isLocalRing (hc : IsLimit c) (j : J) [IsLocalRing (F.obj j)] + (hj : ∀ (x : c.pt), IsUnit (c.π.app j x) → ∀ (i : J), ∃ (k : J) (f : i ⟶ k) (g : j ⟶ k), + IsLocalHom (F.map f).hom ∧ F.map f (c.π.app i x) = F.map g (c.π.app j x)) : + IsLocalRing c.pt := by + have := π_isLocalHom F hc j hj + apply RingHom.domain_isLocalRing (c.π.app j).hom + +end Limits + section Equalizer variable {A B : CommRingCat.{u}} (f g : A ⟶ B) @@ -357,55 +408,44 @@ def equalizerForkIsLimit : IsLimit (equalizerFork f g) := by ext x exact Subtype.ext <| RingHom.congr_fun (congrArg Hom.hom hm) x -instance : IsLocalHom (equalizerFork f g).ι.hom := by - constructor - rintro ⟨a, h₁ : _ = _⟩ (⟨⟨x, y, h₃, h₄⟩, rfl : x = _⟩ : IsUnit a) - have : y ∈ RingHom.eqLocus f.hom g.hom := by - apply (f.hom.isUnit_map ⟨⟨x, y, h₃, h₄⟩, rfl⟩ : IsUnit (f x)).mul_left_inj.mp - conv_rhs => rw [h₁] - rw [← f.hom.map_mul, ← g.hom.map_mul, h₄, f.hom.map_one, g.hom.map_one] - rw [isUnit_iff_exists_inv] - exact ⟨⟨y, this⟩, Subtype.ext h₃⟩ - -@[instance] -theorem equalizer_ι_isLocalHom (F : WalkingParallelPair ⥤ CommRingCat.{u}) : - IsLocalHom (limit.π F WalkingParallelPair.zero).hom := by - have := limMap_π (diagramIsoParallelPair F).hom WalkingParallelPair.zero - rw [← IsIso.comp_inv_eq] at this - rw [← this] - rw [← limit.isoLimitCone_hom_π - ⟨_, - equalizerForkIsLimit (F.map WalkingParallelPairHom.left) - (F.map WalkingParallelPairHom.right)⟩ - WalkingParallelPair.zero] - change IsLocalHom ((lim.map _ ≫ _ ≫ (equalizerFork _ _).ι) ≫ _).hom - infer_instance +instance : IsLocalHom (equalizerFork f g).ι.hom := + inferInstanceAs <| IsLocalHom (f.hom.eqLocus g.hom).subtype -open CategoryTheory.Limits.WalkingParallelPair Opposite +open WalkingParallelPair WalkingParallelPairHom Opposite -open CategoryTheory.Limits.WalkingParallelPairHom +instance equalizer_ι_isLocalHom (F : WalkingParallelPair ⥤ CommRingCat.{u}) : + IsLocalHom (limit.π F WalkingParallelPair.zero).hom := by + refine Limits.π_isLocalHom _ (limit.isLimit _) zero fun x hx i ↦ ?_ + rcases i with _ | _ + · exact ⟨zero, 𝟙 _, 𝟙 _, inferInstance, by simp⟩ + · refine ⟨one, 𝟙 _, left, inferInstance, ?_⟩ + simp only [CategoryTheory.Functor.map_id, hom_id, limit.cone_x, limit.cone_π, RingHom.id_apply] + exact (limit.w_apply F left x).symm + +theorem equalizer_limit_isLocalRing (F : WalkingParallelPair ⥤ CommRingCat.{u}) + [IsLocalRing (F.obj zero)] : IsLocalRing ↑(limit F) := + RingHom.domain_isLocalRing (limit.π F WalkingParallelPair.zero).hom -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in instance equalizer_ι_isLocalHom' (F : WalkingParallelPairᵒᵖ ⥤ CommRingCat.{u}) : - IsLocalHom (limit.π F (Opposite.op WalkingParallelPair.one)).hom := by - have := limit.isoLimitCone_inv_π - ⟨_, IsLimit.whiskerEquivalence (limit.isLimit F) walkingParallelPairOpEquiv⟩ - WalkingParallelPair.zero - dsimp at this - rw [← this] - -- note: this was not needed before https://github.com/leanprover-community/mathlib4/pull/19757 - have : IsLocalHom (limit.π (walkingParallelPairOp ⋙ F) zero).hom := by infer_instance - infer_instance + IsLocalHom (limit.π F (op one)).hom := by + refine Limits.π_isLocalHom _ (limit.isLimit _) (op one) fun x hx i ↦ ?_ + rcases i with _ | _ + · refine ⟨op zero, 𝟙 _, op left, inferInstance, ?_⟩ + simp only [CategoryTheory.Functor.map_id, hom_id, limit.cone_x, limit.cone_π, + RingHom.id_apply] + exact (limit.w_apply F (op left) x).symm + · exact ⟨op one, 𝟙 _, 𝟙 _, inferInstance, by simp⟩ end Equalizer section Pullback +variable {A B C : CommRingCat.{u}} + /-- In the category of `CommRingCat`, the pullback of `f : A ⟶ C` and `g : B ⟶ C` is the `eqLocus` of the two maps `A × B ⟶ C`. This is the constructed pullback cone. -/ -def pullbackCone {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) : PullbackCone f g := +def pullbackCone (f : A ⟶ C) (g : B ⟶ C) : PullbackCone f g := PullbackCone.mk (CommRingCat.ofHom <| (RingHom.fst A B).comp @@ -418,7 +458,7 @@ def pullbackCone {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) : Pullbac simpa [CommRingCat.ofHom] using e) /-- The constructed pullback cone is indeed the limit. -/ -def pullbackConeIsLimit {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) : +def pullbackConeIsLimit (f : A ⟶ C) (g : B ⟶ C) : IsLimit (pullbackCone f g) := by fapply PullbackCone.IsLimit.mk · intro s @@ -439,6 +479,21 @@ def pullbackConeIsLimit {A B C : CommRingCat.{u}} (f : A ⟶ C) (g : B ⟶ C) : rw [← eq1, ← eq2] rfl +open WalkingCospan + +instance pullbackFst_isLocalHom (f : A ⟶ C) (g : B ⟶ C) [IsLocalHom g.hom] : + IsLocalHom (pullback.fst f g).hom := by + refine Limits.π_isLocalHom _ (limit.isLimit _) left fun x hx i ↦ ?_ + rcases i with _ | _ | _ + · exact ⟨one, 𝟙 _, Hom.inl, inferInstance, by simp; rfl⟩ + · exact ⟨left, 𝟙 _, 𝟙 _, inferInstance, by simp⟩ + · refine ⟨one, Hom.inr, Hom.inl, ‹_›, ?_⟩ + exact DFunLike.congr_fun (congr(Hom.hom $(pullback.condition (f := f) (g := g)))) x |>.symm + +theorem pullback_isLocalRing (f : A ⟶ C) (g : B ⟶ C) [IsLocalHom g.hom] [IsLocalRing A] : + IsLocalRing ↑(pullback f g) := + RingHom.domain_isLocalRing (pullback.fst f g).hom + end Pullback end CommRingCat diff --git a/Mathlib/Algebra/Colimit/DirectLimit.lean b/Mathlib/Algebra/Colimit/DirectLimit.lean index 7898ffcdedf41e..3a000c17f440e7 100644 --- a/Mathlib/Algebra/Colimit/DirectLimit.lean +++ b/Mathlib/Algebra/Colimit/DirectLimit.lean @@ -220,8 +220,12 @@ variable [∀ i j h, MonoidHomClass (T h) (G i) (G j)] [∀ i, MonoidHomClass (H one_mul := one_mul mul_one := mul_one npow n := map _ _ (fun _ ↦ (· ^ n)) fun _ _ _ x ↦ map_pow _ x n - npow_zero := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, pow_zero, one_def i] - npow_succ n := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, pow_succ, mul_def] + npow_zero := DirectLimit.induction _ fun i _ ↦ by + simp_rw [HPow.hPow, Pow.pow] + simp_rw [map_def, pow_zero, one_def i] + npow_succ n := DirectLimit.induction _ fun i _ ↦ by + simp_rw [HPow.hPow, Pow.pow] + simp_rw [map_def, pow_succ, mul_def] @[to_additive] theorem npow_def (i x) (n : ℕ) : ⟦⟨i, x⟩⟧ ^ n = (⟦⟨i, x ^ n⟩⟧ : DirectLimit G f) := rfl @@ -256,11 +260,12 @@ variable [∀ i j h, MonoidHomClass (T h) (G i) (G j)] [∀ i, MonoidHomClass (H zpow n := map _ _ (fun _ ↦ (· ^ n)) fun _ _ _ x ↦ map_zpow _ x n div_eq_mul_inv := DirectLimit.induction₂ _ fun i _ _ ↦ show map₂ .. = _ * map .. by simp_rw [map₂_def, map_def, div_eq_mul_inv, mul_def] - zpow_zero' := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, zpow_zero, one_def i] + zpow_zero' := DirectLimit.induction _ fun i _ ↦ by + simp_rw [HPow.hPow, Pow.pow, map_def, zpow_zero, one_def i] zpow_succ' n := DirectLimit.induction _ fun i x ↦ by - simp_rw [map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ' + simp_rw [HPow.hPow, Pow.pow, map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ' zpow_neg' n := DirectLimit.induction _ fun i x ↦ by - simp_rw +instances [map_def]; congr; apply DivInvMonoid.zpow_neg' + simp_rw [HPow.hPow, Pow.pow, map_def]; congr; apply DivInvMonoid.zpow_neg' inv_mul_cancel := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, mul_def, inv_mul_cancel, one_def i] @@ -341,11 +346,12 @@ instance : GroupWithZero (DirectLimit G f) where zpow n := map _ _ (fun _ ↦ (· ^ n)) fun _ _ _ x ↦ map_zpow₀ _ x n div_eq_mul_inv := DirectLimit.induction₂ _ fun i _ _ ↦ show map₂ .. = _ * map .. by simp_rw [map₂_def, map_def, div_eq_mul_inv, mul_def] - zpow_zero' := DirectLimit.induction _ fun i _ ↦ by simp_rw [map_def, zpow_zero, one_def i] + zpow_zero' := DirectLimit.induction _ fun i _ ↦ by + simp_rw [HPow.hPow, Pow.pow, map_def, zpow_zero, one_def i] zpow_succ' n := DirectLimit.induction _ fun i x ↦ by - simp_rw [map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ' + simp_rw [HPow.hPow, Pow.pow, map_def, mul_def]; congr; apply DivInvMonoid.zpow_succ' zpow_neg' n := DirectLimit.induction _ fun i x ↦ by - simp_rw [map_def]; congr; apply DivInvMonoid.zpow_neg' + simp_rw [HPow.hPow, Pow.pow, map_def]; congr; apply DivInvMonoid.zpow_neg' inv_zero := show ⟦_⟧ = ⟦_⟧ by simp_rw [inv_zero] mul_inv_cancel := DirectLimit.induction _ fun i x ne ↦ by have : x ≠ 0 := by rintro rfl; exact ne (zero_def i).symm diff --git a/Mathlib/Algebra/EuclideanDomain/Basic.lean b/Mathlib/Algebra/EuclideanDomain/Basic.lean index 29d4260d10e80d..ca7cbc827090dc 100644 --- a/Mathlib/Algebra/EuclideanDomain/Basic.lean +++ b/Mathlib/Algebra/EuclideanDomain/Basic.lean @@ -419,7 +419,7 @@ section RingEquiv variable {R S : Type*} [EuclideanDomain R] [CommRing S] /-- If `S` is a nontrivial commutative ring isomorphic to a Euclidean domain - `R` then it is also a Euclidean domain. -/ +`R` then it is also a Euclidean domain. -/ protected abbrev RingEquiv.euclideanDomain (e : S ≃+* R) : EuclideanDomain S where toNontrivial := e.nontrivial quotient a b := e.symm (e a / e b) diff --git a/Mathlib/Algebra/EuclideanDomain/Field.lean b/Mathlib/Algebra/EuclideanDomain/Field.lean index 3ba135fab0f1ae..4d0ea217367aac 100644 --- a/Mathlib/Algebra/EuclideanDomain/Field.lean +++ b/Mathlib/Algebra/EuclideanDomain/Field.lean @@ -11,6 +11,7 @@ public import Mathlib.Algebra.GroupWithZero.Units.Basic /-! # Instances for Euclidean domains + * `Field.toEuclideanDomain`: shows that any field is a Euclidean domain. -/ diff --git a/Mathlib/Algebra/EuclideanDomain/Int.lean b/Mathlib/Algebra/EuclideanDomain/Int.lean index 18b3e76133fd71..578e1b6d52a3c3 100644 --- a/Mathlib/Algebra/EuclideanDomain/Int.lean +++ b/Mathlib/Algebra/EuclideanDomain/Int.lean @@ -12,6 +12,7 @@ public import Mathlib.Algebra.Ring.Int.Defs /-! # Instances for Euclidean domains + * `Int.euclideanDomain`: shows that `ℤ` is a Euclidean domain. -/ diff --git a/Mathlib/Algebra/FreeAlgebra.lean b/Mathlib/Algebra/FreeAlgebra.lean index a3df9a637be7b2..2db44e9cca6e07 100644 --- a/Mathlib/Algebra/FreeAlgebra.lean +++ b/Mathlib/Algebra/FreeAlgebra.lean @@ -201,7 +201,6 @@ instance instMonoidWithZero : MonoidWithZero (FreeAlgebra R X) where mul_assoc := by rintro ⟨⟩ ⟨⟩ ⟨⟩ exact Quot.sound Rel.mul_assoc - one := Quot.mk _ 1 one_mul := by rintro ⟨⟩ exact Quot.sound Rel.one_mul @@ -237,7 +236,6 @@ instance instAddCommMonoid : AddCommMonoid (FreeAlgebra R X) where add_comm := by rintro ⟨⟩ ⟨⟩ exact Quot.sound Rel.add_comm - nsmul := (· • ·) nsmul_zero := by rintro ⟨⟩ change Quot.mk _ (_ * _) = _ @@ -245,7 +243,7 @@ instance instAddCommMonoid : AddCommMonoid (FreeAlgebra R X) where exact Quot.sound Rel.zero_mul nsmul_succ n := by rintro ⟨a⟩ - dsimp +instances only [HSMul.hSMul, instSMul, Quot.map] + dsimp only [HSMul.hSMul, SMul.smul, NSMul.nsmul, Quot.map] rw [map_add, map_one, mk_mul, mk_mul, ← add_one_mul (_ : FreeAlgebra R X)] congr 1 exact Quot.sound Rel.add_scalar diff --git a/Mathlib/Algebra/Group/Action/Defs.lean b/Mathlib/Algebra/Group/Action/Defs.lean index 0859764ee46a7d..54914db1eafdfb 100644 --- a/Mathlib/Algebra/Group/Action/Defs.lean +++ b/Mathlib/Algebra/Group/Action/Defs.lean @@ -284,7 +284,8 @@ variable [SMul M α] /-- Auxiliary definition for `SMul.comp`, `MulAction.compHom`, `DistribMulAction.compHom`, `Module.compHom`, etc. -/ -@[to_additive (attr := simp) /-- Auxiliary definition for `VAdd.comp`, `AddAction.compHom`, etc. -/] +@[to_additive (attr := simp, implicit_reducible) +/-- Auxiliary definition for `VAdd.comp`, `AddAction.compHom`, etc. -/] def comp.smul (g : N → M) (n : N) (a : α) : α := g n • a variable (α) diff --git a/Mathlib/Algebra/Group/Action/Opposite.lean b/Mathlib/Algebra/Group/Action/Opposite.lean index ac0b3d9268b373..0725bd5184a37f 100644 --- a/Mathlib/Algebra/Group/Action/Opposite.lean +++ b/Mathlib/Algebra/Group/Action/Opposite.lean @@ -15,7 +15,7 @@ This file defines the actions on the opposite type `SMul R Mᵐᵒᵖ`, and acti type, `SMul Rᵐᵒᵖ M`. Note that `MulOpposite.smul` is provided in an earlier file as it is needed to -provide the `AddMonoid.nsmul` and `AddCommGroup.zsmul` fields. +provide the `NSMul.nsmul` and `ZSMul.zsmul` fields. ## Notation diff --git a/Mathlib/Algebra/Group/Basic.lean b/Mathlib/Algebra/Group/Basic.lean index 823ca65355f567..c28a5efa016538 100644 --- a/Mathlib/Algebra/Group/Basic.lean +++ b/Mathlib/Algebra/Group/Basic.lean @@ -31,25 +31,22 @@ variable {α β G M : Type*} section ite variable [Pow α β] -@[to_additive (attr := simp) dite_smul] +@[to_additive (attr := simp, to_additive) dite_smul] lemma pow_dite (p : Prop) [Decidable p] (a : α) (b : p → β) (c : ¬ p → β) : a ^ (if h : p then b h else c h) = if h : p then a ^ b h else a ^ c h := by split_ifs <;> rfl -@[to_additive (attr := simp) smul_dite] +@[to_additive (attr := simp, to_additive) smul_dite] lemma dite_pow (p : Prop) [Decidable p] (a : p → α) (b : ¬ p → α) (c : β) : (if h : p then a h else b h) ^ c = if h : p then a h ^ c else b h ^ c := by split_ifs <;> rfl -@[to_additive (attr := simp) ite_smul] +@[to_additive (attr := simp, to_additive) ite_smul] lemma pow_ite (p : Prop) [Decidable p] (a : α) (b c : β) : a ^ (if p then b else c) = if p then a ^ b else a ^ c := pow_dite _ _ _ _ -@[to_additive (attr := simp) smul_ite] +@[to_additive (attr := simp, to_additive) smul_ite] lemma ite_pow (p : Prop) [Decidable p] (a b : α) (c : β) : (if p then a else b) ^ c = if p then a ^ c else b ^ c := dite_pow _ _ _ _ -set_option linter.existingAttributeWarning false in -attribute [to_additive (attr := simp)] dite_smul smul_dite ite_smul smul_ite - end ite section Semigroup diff --git a/Mathlib/Algebra/Group/Defs.lean b/Mathlib/Algebra/Group/Defs.lean index 8da0e62370f626..d948baeee2b0ef 100644 --- a/Mathlib/Algebra/Group/Defs.lean +++ b/Mathlib/Algebra/Group/Defs.lean @@ -637,38 +637,51 @@ theorem npowRec_eq_npowBinRec : @npowRecAuto = @npowBinRecAuto := by iterate 2 rw [← npowBinRecAuto, ← npowRec_eq_npowBinRec] rfl -/-- An `AddMonoid` is an `AddSemigroup` with an element `0` such that `0 + a = a + 0 = a`. -/ -class AddMonoid (M : Type u) extends AddSemigroup M, AddZeroClass M where +/-- `NSMul` is an implementation detail of `AddMonoid`. It is needed because it is +impossible to extend `SMul ℕ M` and `SMul ℤ M` at the same time. -/ +class NSMul (M : Type u) where /-- Multiplication by a natural number. Set this to `nsmulRec` unless `Module` diamonds are possible. -/ protected nsmul : ℕ → M → M + +/-- `NPow` is an implementation detail of `Monoid`. It is needed because it is +impossible to extend `Pow M ℕ` and `Pow M ℤ` at the same time. -/ +@[to_additive] +class NPow (M : Type u) where + /-- Raising to the power of a natural number. -/ + protected npow : ℕ → M → M + +@[default_instance high, to_additive toSMul] +instance NPow.toPow {M : Type*} [NPow M] : Pow M ℕ := + ⟨fun x n ↦ NPow.npow n x⟩ + +@[to_additive ofSMul] +instance NPow.ofPow {M : Type*} [Pow M ℕ] : NPow M := ⟨fun n x ↦ Pow.pow x n⟩ + +/-- An `AddMonoid` is an `AddSemigroup` with an element `0` such that `0 + a = a + 0 = a`. -/ +class AddMonoid (M : Type u) extends AddSemigroup M, AddZeroClass M, NSMul M where /-- Multiplication by `(0 : ℕ)` gives `0`. -/ - protected nsmul_zero : ∀ x, nsmul 0 x = 0 := by intros; rfl + protected nsmul_zero (x : M) : 0 • x = 0 := by intros; rfl /-- Multiplication by `(n + 1 : ℕ)` behaves as expected. -/ - protected nsmul_succ : ∀ (n : ℕ) (x), nsmul (n + 1) x = nsmul n x + x := by intros; rfl + protected nsmul_succ (n : ℕ) (x : M) : (n + 1) • x = n • x + x := by intros; rfl attribute [instance 150] AddSemigroup.toAdd attribute [instance 50] AddZero.toAdd /-- A `Monoid` is a `Semigroup` with an element `1` such that `1 * a = a * 1 = a`. -/ @[to_additive] -class Monoid (M : Type u) extends Semigroup M, MulOneClass M where - /-- Raising to the power of a natural number. -/ - protected npow : ℕ → M → M := npowRecAuto +class Monoid (M : Type u) extends Semigroup M, MulOneClass M, NPow M where + npow := npowRecAuto /-- Raising to the power `(0 : ℕ)` gives `1`. -/ - protected npow_zero : ∀ x, npow 0 x = 1 := by intros; rfl + protected npow_zero (x : M) : x ^ 0 = 1 := by intros; rfl /-- Raising to the power `(n + 1 : ℕ)` behaves as expected. -/ - protected npow_succ : ∀ (n : ℕ) (x), npow (n + 1) x = npow n x * x := by intros; rfl - -@[default_instance high, to_additive] -instance Monoid.toPow {M : Type*} [Monoid M] : Pow M ℕ := - ⟨fun x n ↦ Monoid.npow n x⟩ + protected npow_succ (n : ℕ) (x : M) : x ^ (n + 1) = x ^ n * x := by intros; rfl section Monoid variable {M : Type*} [Monoid M] {a b c : M} @[to_additive (attr := simp) nsmul_eq_smul] -theorem npow_eq_pow (n : ℕ) (x : M) : Monoid.npow n x = x ^ n := +theorem npow_eq_pow (n : ℕ) (x : M) : NPow.npow n x = x ^ n := rfl @[to_additive] lemma left_inv_eq_right_inv (hba : b * a = 1) (hac : a * c = 1) : b = c := by @@ -941,6 +954,27 @@ field of individual `DivInvMonoid`s constructed using that default value will no `.instance` transparency. -/ def DivInvMonoid.div' {G : Type u} [Monoid G] [Inv G] (a b : G) : G := a * b⁻¹ +/-- `ZSMul` is an implementation detail of `SubNegMonoid`. It is needed because it is +impossible to extend `SMUl ℕ M` and `SMul ℤ M` at the same time. -/ +class ZSMul (G : Type u) where + /-- Multiplication by an integer. + Set this to `zsmulRec` unless `Module` diamonds are possible. -/ + protected zsmul : ℤ → G → G + +/-- `ZPow` is an implementation detail of `DivInvMonoid`. It is needed because it is +impossible to extend `Pow M ℕ` and `Pow M ℤ` at the same time. -/ +@[to_additive] +class ZPow (G : Type u) where + /-- The power operation: `a ^ n = a * ··· * a`; `a ^ (-n) = a⁻¹ * ··· a⁻¹` (`n` times) -/ + protected zpow : ℤ → G → G + +@[to_additive toSMul] +instance ZPow.toPow {M : Type*} [ZPow M] : Pow M ℤ := + ⟨fun x n ↦ ZPow.zpow n x⟩ + +@[to_additive ofSMul] +instance ZPow.ofPow {M : Type*} [Pow M ℤ] : ZPow M := ⟨fun n x ↦ Pow.pow x n⟩ + /-- A `DivInvMonoid` is a `Monoid` with operations `/` and `⁻¹` satisfying `div_eq_mul_inv : ∀ a b, a / b = a * b⁻¹`. @@ -959,19 +993,18 @@ In the same way, adding a `zpow` field makes it possible to avoid definitional f in diamonds. See the definition of `Monoid` and Note [forgetful inheritance] for more explanations on this. -/ -class DivInvMonoid (G : Type u) extends Monoid G, Inv G, Div G where +class DivInvMonoid (G : Type u) extends Monoid G, Inv G, Div G, ZPow G where protected div := DivInvMonoid.div' /-- `a / b := a * b⁻¹` -/ protected div_eq_mul_inv : ∀ a b : G, a / b = a * b⁻¹ := by intros; rfl - /-- The power operation: `a ^ n = a * ··· * a`; `a ^ (-n) = a⁻¹ * ··· a⁻¹` (`n` times) -/ - protected zpow : ℤ → G → G := zpowRec npowRec + zpow := zpowRec npowRec /-- `a ^ 0 = 1` -/ - protected zpow_zero' : ∀ a : G, zpow 0 a = 1 := by intros; rfl + protected zpow_zero' (a : G) : a ^ (0 : ℤ) = 1 := by intros; rfl /-- `a ^ (n + 1) = a ^ n * a` -/ - protected zpow_succ' (n : ℕ) (a : G) : zpow n.succ a = zpow n a * a := by + protected zpow_succ' (n : ℕ) (a : G) : a ^ (n.succ : ℤ) = a ^ (n : ℤ) * a := by intros; rfl /-- `a ^ -(n + 1) = (a ^ (n + 1))⁻¹` -/ - protected zpow_neg' (n : ℕ) (a : G) : zpow (Int.negSucc n) a = (zpow n.succ a)⁻¹ := by intros; rfl + protected zpow_neg' (n : ℕ) (a : G) : a ^ Int.negSucc n = (a ^ (n.succ : ℤ))⁻¹ := by intros; rfl /-- In a class equipped with instances of both `AddMonoid` and `Neg`, this definition records what the default definition for `Sub` would be: `a + -b`. This is later provided as the default value @@ -1001,29 +1034,18 @@ In the same way, adding a `zsmul` field makes it possible to avoid definitional in diamonds. See the definition of `AddMonoid` and Note [forgetful inheritance] for more explanations on this. -/ -class SubNegMonoid (G : Type u) extends AddMonoid G, Neg G, Sub G where +class SubNegMonoid (G : Type u) extends AddMonoid G, Neg G, Sub G, ZSMul G where protected sub := SubNegMonoid.sub' protected sub_eq_add_neg : ∀ a b : G, a - b = a + -b := by intros; rfl - /-- Multiplication by an integer. - Set this to `zsmulRec` unless `Module` diamonds are possible. -/ - protected zsmul : ℤ → G → G - protected zsmul_zero' : ∀ a : G, zsmul 0 a = 0 := by intros; rfl + protected zsmul_zero' (a : G) : (0 : ℤ) • a = 0 := by intros; rfl protected zsmul_succ' (n : ℕ) (a : G) : - zsmul n.succ a = zsmul n a + a := by + (n.succ : ℤ) • a = (n : ℤ) • a + a := by intros; rfl - protected zsmul_neg' (n : ℕ) (a : G) : zsmul (Int.negSucc n) a = -zsmul n.succ a := by + protected zsmul_neg' (n : ℕ) (a : G) : (Int.negSucc n) • a = -((n.succ : ℤ) • a) := by intros; rfl attribute [to_additive SubNegMonoid] DivInvMonoid -instance DivInvMonoid.toZPow {M} [DivInvMonoid M] : Pow M ℤ := - ⟨fun x n ↦ DivInvMonoid.zpow n x⟩ - -instance SubNegMonoid.toZSMul {M} [SubNegMonoid M] : SMul ℤ M := - ⟨SubNegMonoid.zsmul⟩ - -attribute [to_additive existing] DivInvMonoid.toZPow - /-- A group is called *cyclic* if it is generated by a single element. -/ class IsAddCyclic (G : Type u) [SMul ℤ G] : Prop where protected exists_zsmul_surjective : ∃ g : G, Function.Surjective (· • g : ℤ → G) @@ -1043,7 +1065,7 @@ section DivInvMonoid variable [DivInvMonoid G] @[to_additive (attr := simp) zsmul_eq_smul] theorem zpow_eq_pow (n : ℤ) (x : G) : - DivInvMonoid.zpow n x = x ^ n := + ZPow.zpow n x = x ^ n := rfl @[to_additive (attr := simp) zero_zsmul] theorem zpow_zero (a : G) : a ^ (0 : ℤ) = 1 := diff --git a/Mathlib/Algebra/Group/EvenFunction.lean b/Mathlib/Algebra/Group/EvenFunction.lean index 0547bcd2a4bc90..fac96be08157c8 100644 --- a/Mathlib/Algebra/Group/EvenFunction.lean +++ b/Mathlib/Algebra/Group/EvenFunction.lean @@ -34,12 +34,18 @@ protected def Even (f : α → β) : Prop := ∀ a, f (-a) = f a /-- A function `f` is _odd_ if it satisfies `f (-x) = -f x` for all `x`. -/ protected def Odd [Neg β] (f : α → β) : Prop := ∀ a, f (-a) = -(f a) +/-- An even function `f` satisfies `f (-x) = f x`. -/ +lemma Even.eq {f : α → β} (hf : f.Even) (x : α) : f (-x) = f x := hf x + /-- Any constant function is even. -/ lemma Even.const (b : β) : Function.Even (fun _ : α ↦ b) := fun _ ↦ rfl /-- The zero function is even. -/ lemma Even.zero [Zero β] : Function.Even (fun (_ : α) ↦ (0 : β)) := Even.const 0 +/-- An odd function `f` satisfies `f (-x) = -f x`. -/ +lemma Odd.eq [Neg β] {f : α → β} (hf : f.Odd) (x : α) : f (-x) = -f x := hf x + /-- The zero function is odd. -/ lemma Odd.zero [NegZeroClass β] : Function.Odd (fun (_ : α) ↦ (0 : β)) := fun _ ↦ neg_zero.symm diff --git a/Mathlib/Algebra/Group/Ext.lean b/Mathlib/Algebra/Group/Ext.lean index 3874fa32780c75..279b4d658c69ac 100644 --- a/Mathlib/Algebra/Group/Ext.lean +++ b/Mathlib/Algebra/Group/Ext.lean @@ -47,7 +47,7 @@ theorem Monoid.ext {M : Type u} ⦃m₁ m₂ : Monoid M⦄ have : m₁.npow = m₂.npow := by ext n x exact @MonoidHom.map_pow M M m₁ m₂ f x n - rcases m₁ with @⟨@⟨⟨_⟩⟩, ⟨_⟩⟩ + rcases m₁ with @⟨@⟨⟨_⟩⟩, ⟨_⟩, _, _, ⟨_⟩⟩ congr @[to_additive] @@ -134,7 +134,7 @@ theorem DivInvMonoid.ext {M : Type*} ⦃m₁ m₂ : DivInvMonoid M⦄ exact (@div_eq_mul_inv _ m₁ a b).trans (((congr_fun (congr_fun h_mul a) _).trans (congr_arg _ (congr_fun h_inv b))).trans (@div_eq_mul_inv _ m₂ a b).symm) - rcases m₁ with @⟨_, ⟨_⟩, ⟨_⟩⟩ + rcases m₁ with @⟨_, ⟨_⟩, ⟨_⟩, ⟨_⟩⟩ congr @[to_additive] diff --git a/Mathlib/Algebra/Group/Fin/Tuple.lean b/Mathlib/Algebra/Group/Fin/Tuple.lean index 7d566c015fa76d..5c332bc478acc6 100644 --- a/Mathlib/Algebra/Group/Fin/Tuple.lean +++ b/Mathlib/Algebra/Group/Fin/Tuple.lean @@ -103,6 +103,9 @@ variable [Zero α] @[simp] lemma zero_empty : (0 : Fin 0 → α) = ![] := empty_eq _ +@[simp] lemma finZeroElim_eq_zero : (@finZeroElim fun _ ↦ α) = 0 := by + rw [Matrix.empty_eq finZeroElim, Matrix.zero_empty] + @[simp] lemma cons_zero_zero : vecCons (0 : α) (0 : Fin n → α) = 0 := by ext i; exact i.cases rfl (by simp) diff --git a/Mathlib/Algebra/Group/Graph.lean b/Mathlib/Algebra/Group/Graph.lean index afff321eb2af61..4253578e4e5926 100644 --- a/Mathlib/Algebra/Group/Graph.lean +++ b/Mathlib/Algebra/Group/Graph.lean @@ -51,7 +51,6 @@ def mgraph (f : G →* H) : Submonoid (G × H) where -- TODO: Can `to_additive` be smarter about `simps`? attribute [simps! coe] mgraph attribute [simps! coe] AddMonoidHom.mgraph -set_option linter.existingAttributeWarning false in attribute [to_additive existing] coe_mgraph @[to_additive (attr := simp)] @@ -163,7 +162,6 @@ def graph (f : G →* H) : Subgroup (G × H) where -- TODO: Can `to_additive` be smarter about `simps`? attribute [simps! coe toSubmonoid] graph attribute [simps! coe toAddSubmonoid] AddMonoidHom.graph -set_option linter.existingAttributeWarning false in attribute [to_additive existing] coe_graph graph_toSubmonoid @[to_additive] diff --git a/Mathlib/Algebra/Group/Hom/Defs.lean b/Mathlib/Algebra/Group/Hom/Defs.lean index ab1fcb1515dab3..87ff9898f913a9 100644 --- a/Mathlib/Algebra/Group/Hom/Defs.lean +++ b/Mathlib/Algebra/Group/Hom/Defs.lean @@ -714,6 +714,23 @@ theorem map_exists_left_inv (f : F) {x : M} (hx : ∃ y, y * x = 1) : ∃ y, y * @[deprecated (since := "2025-12-14")] alias isDedekindFiniteMonoid_of_injective := IsDedekindFiniteMonoid.of_injective +@[to_additive] +instance {M N : Type*} [Monoid M] [LeftCancelMonoid N] : MonoidHomClass (M →ₙ* N) M N where + map_mul := MulHom.map_mul' + map_one f := by + have h : f 1 * 1 = f 1 * f 1 := by simpa using f.map_mul' 1 1 + exact (mul_left_cancel h).symm + +@[to_additive] +instance {M N : Type*} [Monoid M] [RightCancelMonoid N] : MonoidHomClass (M →ₙ* N) M N where + map_mul := MulHom.map_mul' + map_one f := by + have h : 1 * f 1 = f 1 * f 1 := by simpa using f.map_mul' 1 1 + exact (mul_right_cancel h).symm + +@[to_additive] +instance {M N : Type*} [Monoid M] [CancelMonoid N] : MonoidHomClass (M →ₙ* N) M N where + end MonoidHom /-- The identity map from a type with 1 to itself. -/ diff --git a/Mathlib/Algebra/Group/Int/Defs.lean b/Mathlib/Algebra/Group/Int/Defs.lean index 09c73fe1a74bd5..9e06bbccfe336d 100644 --- a/Mathlib/Algebra/Group/Int/Defs.lean +++ b/Mathlib/Algebra/Group/Int/Defs.lean @@ -47,8 +47,8 @@ instance instAddCommGroup : AddCommGroup ℤ where zsmul := (· * ·) zsmul_zero' := Int.zero_mul zsmul_succ' m n := by - simp only [natCast_succ, Int.add_mul, Int.add_comm, Int.one_mul] - zsmul_neg' m n := by simp only [negSucc_eq, natCast_succ, Int.neg_mul] + simp only [HSMul.hSMul, SMul.smul, natCast_succ, Int.add_mul, Int.add_comm, Int.one_mul] + zsmul_neg' m n := by simp only [HSMul.hSMul, SMul.smul, negSucc_eq, natCast_succ, Int.neg_mul] sub_eq_add_neg _ _ := Int.sub_eq_add_neg -- This instance can also be found from the `LinearOrderedCommMonoidWithZero ℤ` instance by diff --git a/Mathlib/Algebra/Group/Opposite.lean b/Mathlib/Algebra/Group/Opposite.lean index 68a9347be830d8..5668d2f7882e82 100644 --- a/Mathlib/Algebra/Group/Opposite.lean +++ b/Mathlib/Algebra/Group/Opposite.lean @@ -142,6 +142,7 @@ instance instDivInvMonoid [DivInvMonoid α] : DivInvMonoid αᵐᵒᵖ where zpow n a := op <| a.unop ^ n zpow_zero' _ := unop_injective <| zpow_zero _ zpow_succ' _ _ := unop_injective <| by + simp_rw [HPow.hPow, Pow.pow] rw [unop_op, zpow_natCast, pow_succ', unop_mul, unop_op, zpow_natCast] zpow_neg' _ _ := unop_injective <| DivInvMonoid.zpow_neg' _ _ diff --git a/Mathlib/Algebra/Group/Prod.lean b/Mathlib/Algebra/Group/Prod.lean index 7c8bf82d187fa0..12b8deaed8d2d4 100644 --- a/Mathlib/Algebra/Group/Prod.lean +++ b/Mathlib/Algebra/Group/Prod.lean @@ -87,7 +87,7 @@ instance instMulOneClass [MulOneClass M] [MulOneClass N] : MulOneClass (M × N) @[to_additive] instance instMonoid [Monoid M] [Monoid N] : Monoid (M × N) := - { npow := fun z a => ⟨Monoid.npow z a.1, Monoid.npow z a.2⟩, + { npow := fun z a => ⟨NPow.npow z a.1, NPow.npow z a.2⟩, npow_zero := fun _ => Prod.ext (Monoid.npow_zero _) (Monoid.npow_zero _), npow_succ := fun _ _ => Prod.ext (Monoid.npow_succ _ _) (Monoid.npow_succ _ _), one_mul := by simp, @@ -102,7 +102,7 @@ instance instIsMulTorsionFree [Monoid M] [Monoid N] [IsMulTorsionFree M] [IsMulT @[to_additive Prod.subNegMonoid] instance [DivInvMonoid G] [DivInvMonoid H] : DivInvMonoid (G × H) where div_eq_mul_inv _ _ := by ext <;> exact div_eq_mul_inv .. - zpow z a := ⟨DivInvMonoid.zpow z a.1, DivInvMonoid.zpow z a.2⟩ + zpow z a := ⟨ZPow.zpow z a.1, ZPow.zpow z a.2⟩ zpow_zero' _ := by ext <;> exact DivInvMonoid.zpow_zero' _ zpow_succ' _ _ := by ext <;> exact DivInvMonoid.zpow_succ' .. zpow_neg' _ _ := by ext <;> exact DivInvMonoid.zpow_neg' .. diff --git a/Mathlib/Algebra/Group/Subgroup/Basic.lean b/Mathlib/Algebra/Group/Subgroup/Basic.lean index 8d12154ce3c9d3..0799e6710ca3cd 100644 --- a/Mathlib/Algebra/Group/Subgroup/Basic.lean +++ b/Mathlib/Algebra/Group/Subgroup/Basic.lean @@ -729,6 +729,11 @@ lemma ker_snd : ker (snd G G') = .prod ⊤ ⊥ := SetLike.ext fun _ => (iff_of_e end Ker +@[to_additive (attr := simp) range_prodMap] +lemma range_prodMap {G' N' : Type*} [Group G'] [Group N'] (f : G →* N) (g : G' →* N') : + (f.prodMap g).range = f.range.prod g.range := + SetLike.coe_injective Set.range_prodMap + end MonoidHom namespace Subgroup @@ -939,6 +944,15 @@ theorem Normal.of_map_subtype {K : Subgroup G} {L : Subgroup K} (n : (Subgroup.map K.subtype L).Normal) : L.Normal := n.of_map_injective K.subtype_injective +theorem normal_comap_iff_of_surjective {f : G →* N} (hf : Function.Surjective f) {H : Subgroup N} : + (H.comap f).Normal ↔ H.Normal := by + rw [← normalizer_eq_top_iff, ← comap_normalizer_eq_of_surjective H hf, ← comap_top f, + (comap_injective hf).eq_iff, normalizer_eq_top_iff] + +theorem _root_.MulEquiv.normal_map_iff {f : G ≃* G'} {H : Subgroup G} : + (H.map (f : G →* G')).Normal ↔ H.Normal := by + rw [map_equiv_eq_comap_symm, normal_comap_iff_of_surjective f.symm.surjective] + section SubgroupNormal @[to_additive] @@ -1074,26 +1088,34 @@ def noncenter (G : Type*) [Monoid G] : Set (ConjClasses G) := end ConjClasses +namespace AddSubgroup + +variable {M : Type*} [AddGroup M] (I : AddSubgroup M) (G : Type*) + [Group G] [MulAction G M] + /-- Suppose `G` acts on `M` and `I` is a subgroup of `M`. The inertia subgroup of `I` is the subgroup of `G` whose action is trivial mod `I`. -/ -def AddSubgroup.inertia {M : Type*} [AddGroup M] (I : AddSubgroup M) (G : Type*) - [Group G] [MulAction G M] : Subgroup G where +def inertia : Subgroup G where carrier := { σ | ∀ x, σ • x - x ∈ I } mul_mem' {a b} ha hb x := by simpa [mul_smul] using add_mem (ha (b • x)) (hb x) one_mem' := by simp [zero_mem] inv_mem' {a} ha x := by simpa using sub_mem_comm_iff.mp (ha (a⁻¹ • x)) -@[simp] lemma AddSubgroup.mem_inertia {M : Type*} [AddGroup M] {I : AddSubgroup M} {G : Type*} - [Group G] [MulAction G M] {σ : G} : σ ∈ I.inertia G ↔ ∀ x, σ • x - x ∈ I := .rfl +variable {I G} in +@[simp] +lemma mem_inertia {σ : G} : σ ∈ I.inertia G ↔ ∀ x, σ • x - x ∈ I := .rfl +variable {G} in @[simp] -lemma AddSubgroup.subgroupOf_inertia {M : Type*} [AddGroup M] (I : AddSubgroup M) - {G : Type*} [Group G] [MulAction G M] (H : Subgroup G) : - (I.inertia G).subgroupOf H = I.inertia H := +lemma subgroupOf_inertia (H : Subgroup G) : (I.inertia G).subgroupOf H = I.inertia H := rfl +variable {I G} in +lemma coe_mem_inertia {H : Subgroup G} {σ : H} : ↑σ ∈ I.inertia G ↔ σ ∈ I.inertia H := .rfl + +variable {G} in @[simp] -lemma AddSubgroup.inertia_map_subtype {M : Type*} [AddGroup M] (I : AddSubgroup M) - {G : Type*} [Group G] [MulAction G M] (H : Subgroup G) : - (I.inertia H).map H.subtype = I.inertia G ⊓ H := by +lemma inertia_map_subtype (H : Subgroup G) : (I.inertia H).map H.subtype = I.inertia G ⊓ H := by rw [← AddSubgroup.subgroupOf_inertia, Subgroup.subgroupOf_map_subtype] + +end AddSubgroup diff --git a/Mathlib/Algebra/Group/Subgroup/Ker.lean b/Mathlib/Algebra/Group/Subgroup/Ker.lean index 5f0826515c0693..17796f56170410 100644 --- a/Mathlib/Algebra/Group/Subgroup/Ker.lean +++ b/Mathlib/Algebra/Group/Subgroup/Ker.lean @@ -82,6 +82,11 @@ theorem mem_range {f : G →* N} {y : N} : y ∈ f.range ↔ ∃ x, f x = y := @[to_additive] theorem range_eq_map (f : G →* N) : f.range = (⊤ : Subgroup G).map f := by ext; simp +@[to_additive (attr := simp)] +theorem comap_range_self (f : G →* N) : f.range.comap f = ⊤ := by + ext + simp + @[to_additive] instance _root_.Subgroup.range_isMulCommutative {G : Type*} [Group G] [IsMulCommutative G] {N : Type*} [Group N] (f : G →* N) : @@ -270,6 +275,10 @@ theorem comap_ker {P : Type*} [MulOneClass P] (g : N →* P) (f : G →* N) : theorem comap_bot (f : G →* N) : (⊥ : Subgroup N).comap f = f.ker := rfl +@[to_additive] +theorem ker_le_comap (f : G →* N) (H : Subgroup N) : f.ker ≤ H.comap f := + comap_mono bot_le + @[to_additive (attr := simp)] theorem ker_restrict (f : G →* M) : (f.restrict K).ker = f.ker.subgroupOf K := rfl @@ -401,6 +410,10 @@ theorem map_eq_bot_iff {f : G →* N} : H.map f = ⊥ ↔ H ≤ f.ker := theorem map_eq_bot_iff_of_injective {f : G →* N} (hf : Function.Injective f) : H.map f = ⊥ ↔ H = ⊥ := by rw [map_eq_bot_iff, f.ker_eq_bot hf, le_bot_iff] +@[to_additive (attr := simp)] +theorem map_ker_self (f : G →* N) : f.ker.map f = ⊥ := by + rw [map_eq_bot_iff] + open MonoidHom variable (f : G →* N) @@ -458,6 +471,15 @@ theorem comap_lt_comap_of_surjective {f : G →* N} {K L : Subgroup N} (hf : Fun theorem comap_injective {f : G →* N} (h : Function.Surjective f) : Function.Injective (comap f) := fun K L => by simp only [le_antisymm_iff, comap_le_comap_of_surjective h, imp_self] +@[to_additive (attr := simp)] +theorem comap_eq_ker {f : G →* N} {H : Subgroup N} : H.comap f = f.ker ↔ Disjoint H f.range := by + rw [← H.ker_le_comap f |>.ge_iff_eq', ← map_eq_bot_iff, map_comap_eq, disjoint_iff, inf_comm] + +@[to_additive] +theorem comap_eq_ker_of_surjective {f : G →* N} (hf : Surjective f) {H : Subgroup N} : + H.comap f = f.ker ↔ H = ⊥ := by + rw [comap_eq_ker, f.range_eq_top_of_surjective hf, disjoint_top] + @[to_additive] theorem comap_map_eq_self {f : G →* N} {H : Subgroup G} (h : f.ker ≤ H) : comap f (map f H) = H := by diff --git a/Mathlib/Algebra/Group/Submonoid/Basic.lean b/Mathlib/Algebra/Group/Submonoid/Basic.lean index 133c0073fadcfa..e080248b0791ed 100644 --- a/Mathlib/Algebra/Group/Submonoid/Basic.lean +++ b/Mathlib/Algebra/Group/Submonoid/Basic.lean @@ -361,6 +361,10 @@ theorem IsUnit.mem_submonoid_iff {M : Type*} [Monoid M] (a : M) : end IsUnit +@[simp] lemma Submonoid.commute_coe_coe {S M : Type*} [Mul M] [SetLike S M] + [MulMemClass S M] {s : S} {x y : s} : Commute (x : M) (y : M) ↔ Commute x y := by + simp [commute_iff_eq, Subtype.ext_iff] + namespace MonoidHom open Submonoid diff --git a/Mathlib/Algebra/Group/Submonoid/Membership.lean b/Mathlib/Algebra/Group/Submonoid/Membership.lean index 9c81e885c1eef0..0e5c544e78d9ea 100644 --- a/Mathlib/Algebra/Group/Submonoid/Membership.lean +++ b/Mathlib/Algebra/Group/Submonoid/Membership.lean @@ -361,8 +361,12 @@ abbrev groupPowers {x : M} {n : ℕ} (hpos : 0 < n) (hx : x ^ n = 1) : Group (po simp only [coe_one, coe_mul, SubmonoidClass.coe_pow] rw [← pow_succ, Nat.sub_add_cancel hpos, ← pow_mul, mul_comm, pow_mul, hx, one_pow] zpow z x := x ^ z.natMod n - zpow_zero' z := by simp only [Int.natMod, Int.zero_emod, Int.toNat_zero, pow_zero] - zpow_neg' m x := Subtype.ext <| by + zpow_zero' z := by + simp_rw [HPow.hPow, Pow.pow] + simp only [Int.natMod, Int.zero_emod, Int.toNat_zero, pow_zero] + zpow_neg' m x := by + change x ^ (Int.natMod _ n) = (x ^ (Int.natMod _ n)) ^ (n - 1) + ext obtain ⟨_, k, rfl⟩ := x simp only [← pow_mul, Int.natMod, SubmonoidClass.coe_pow] rw [Int.negSucc_eq, ← Int.natCast_succ, ← Int.add_mul_emod_self_right (b := (m + 1 : ℕ))] @@ -372,6 +376,7 @@ abbrev groupPowers {x : M} {n : ℕ} (hpos : 0 < n) (hx : x ^ n = 1) : Group (po rw [mul_comm, pow_mul, ← pow_eq_pow_mod _ hx, mul_comm k, mul_assoc, pow_mul _ (_ % _), ← pow_eq_pow_mod _ hx, pow_mul, pow_mul] zpow_succ' m x := Subtype.ext <| by + simp_rw [HPow.hPow, Pow.pow] obtain ⟨_, k, rfl⟩ := x simp only [← pow_mul, Int.natMod, SubmonoidClass.coe_pow, coe_mul] norm_cast diff --git a/Mathlib/Algebra/Group/Submonoid/Operations.lean b/Mathlib/Algebra/Group/Submonoid/Operations.lean index 82fdff1c1df514..d498ccecce2a86 100644 --- a/Mathlib/Algebra/Group/Submonoid/Operations.lean +++ b/Mathlib/Algebra/Group/Submonoid/Operations.lean @@ -677,6 +677,12 @@ theorem map_mrange (g : N →* P) (f : M →* N) : (mrange f).map g = mrange (co theorem mrange_eq_top {f : F} : mrange f = (⊤ : Submonoid N) ↔ Surjective f := SetLike.ext'_iff.trans <| Iff.trans (by rw [coe_mrange, coe_top]) Set.range_eq_univ +@[to_additive (attr := simp) mrange_prodMap] +lemma mrange_prodMap {M' N' : Type*} [MulOneClass M'] [MulOneClass N'] (f : M →* N) + (g : M' →* N') : + MonoidHom.mrange (f.prodMap g) = (MonoidHom.mrange f).prod (MonoidHom.mrange g) := + SetLike.coe_injective Set.range_prodMap + /-- The range of a surjective monoid hom is the whole of the codomain. -/ @[to_additive (attr := simp) /-- The range of a surjective `AddMonoid` hom is the whole of the codomain. -/] diff --git a/Mathlib/Algebra/Group/Submonoid/Units.lean b/Mathlib/Algebra/Group/Submonoid/Units.lean index bfd92d0a4dd51a..43f53ae8b81f86 100644 --- a/Mathlib/Algebra/Group/Submonoid/Units.lean +++ b/Mathlib/Algebra/Group/Submonoid/Units.lean @@ -189,7 +189,7 @@ S.unitsEquivUnitsType.trans unitsTypeEquivIsUnitSubmonoid end Units instance instSubsingletonUnits [Subsingleton Mˣ] {S : Submonoid M} : Subsingleton Sˣ := - .units_of_isUnit fun _a ha ↦ Subtype.ext (ha.map S.subtype).eq_one + .units_of_isUnit fun _a ha ↦ Subtype.ext (ha.map S.subtype).eq_one end Submonoid diff --git a/Mathlib/Algebra/Group/TypeTags/Basic.lean b/Mathlib/Algebra/Group/TypeTags/Basic.lean index 07c87018aaf344..a07b3ee9363195 100644 --- a/Mathlib/Algebra/Group/TypeTags/Basic.lean +++ b/Mathlib/Algebra/Group/TypeTags/Basic.lean @@ -261,17 +261,15 @@ instance Multiplicative.mulOneClass [AddZeroClass α] : MulOneClass (Multiplicat one_mul := @zero_add α _ mul_one := @add_zero α _ -instance Additive.addMonoid [h : Monoid α] : AddMonoid (Additive α) := - { Additive.addZeroClass, Additive.addSemigroup with - nsmul := @Monoid.npow α h - nsmul_zero := @Monoid.npow_zero α h - nsmul_succ := @Monoid.npow_succ α h } +instance Additive.addMonoid [h : Monoid α] : AddMonoid (Additive α) where + nsmul := h.npow + nsmul_zero := h.npow_zero + nsmul_succ := h.npow_succ -instance Multiplicative.monoid [h : AddMonoid α] : Monoid (Multiplicative α) := - { Multiplicative.mulOneClass, Multiplicative.semigroup with - npow := @AddMonoid.nsmul α h - npow_zero := @AddMonoid.nsmul_zero α h - npow_succ := @AddMonoid.nsmul_succ α h } +instance Multiplicative.monoid [h : AddMonoid α] : Monoid (Multiplicative α) where + npow := h.nsmul + npow_zero := h.nsmul_zero + npow_succ := h.nsmul_succ @[simp] theorem ofMul_pow [Monoid α] (n : ℕ) (a : α) : ofMul (a ^ n) = n • ofMul a := @@ -415,21 +413,19 @@ instance Additive.involutiveNeg [InvolutiveInv α] : InvolutiveNeg (Additive α) instance Multiplicative.involutiveInv [InvolutiveNeg α] : InvolutiveInv (Multiplicative α) := { Multiplicative.inv with inv_inv := @neg_neg α _ } -instance Additive.subNegMonoid [DivInvMonoid α] : SubNegMonoid (Additive α) := - { Additive.neg, Additive.sub, Additive.addMonoid with - sub_eq_add_neg := @div_eq_mul_inv α _ - zsmul := @DivInvMonoid.zpow α _ - zsmul_zero' := @DivInvMonoid.zpow_zero' α _ - zsmul_succ' := @DivInvMonoid.zpow_succ' α _ - zsmul_neg' := @DivInvMonoid.zpow_neg' α _ } - -instance Multiplicative.divInvMonoid [SubNegMonoid α] : DivInvMonoid (Multiplicative α) := - { Multiplicative.inv, Multiplicative.div, Multiplicative.monoid with - div_eq_mul_inv := @sub_eq_add_neg α _ - zpow := @SubNegMonoid.zsmul α _ - zpow_zero' := @SubNegMonoid.zsmul_zero' α _ - zpow_succ' := @SubNegMonoid.zsmul_succ' α _ - zpow_neg' := @SubNegMonoid.zsmul_neg' α _ } +instance Additive.subNegMonoid [h : DivInvMonoid α] : SubNegMonoid (Additive α) where + sub_eq_add_neg := h.div_eq_mul_inv + zsmul := h.zpow + zsmul_zero' := h.zpow_zero' + zsmul_succ' := h.zpow_succ' + zsmul_neg' := h.zpow_neg' + +instance Multiplicative.divInvMonoid [h : SubNegMonoid α] : DivInvMonoid (Multiplicative α) where + div_eq_mul_inv := h.sub_eq_add_neg + zpow := h.zsmul + zpow_zero' := h.zsmul_zero' + zpow_succ' := h.zsmul_succ' + zpow_neg' := h.zsmul_neg' @[simp] theorem ofMul_zpow [DivInvMonoid α] (z : ℤ) (a : α) : ofMul (a ^ z) = z • ofMul a := diff --git a/Mathlib/Algebra/Group/Units/Defs.lean b/Mathlib/Algebra/Group/Units/Defs.lean index 96dd3018a9281f..3a7b7dd89014d1 100644 --- a/Mathlib/Algebra/Group/Units/Defs.lean +++ b/Mathlib/Algebra/Group/Units/Defs.lean @@ -233,8 +233,8 @@ instance instMonoid : Monoid αˣ := inv := a⁻¹ ^ n val_inv := by rw [← a.commute_coe_inv.mul_pow]; simp inv_val := by rw [← a.commute_inv_coe.mul_pow]; simp } - npow_zero := fun a ↦ by ext; simp - npow_succ := fun n a ↦ by ext; simp [pow_succ] } + npow_zero := fun a ↦ by simp only [HPow.hPow, Pow.pow]; ext; simp + npow_succ := fun n a ↦ by simp only [HPow.hPow, Pow.pow]; ext; simp [pow_succ] } /-- Units of a monoid have division -/ @[to_additive /-- Additive units of an additive monoid have subtraction. -/] @@ -251,9 +251,9 @@ instance instDivInvMonoid : DivInvMonoid αˣ where zpow := fun n a ↦ match n, a with | Int.ofNat n, a => a ^ n | Int.negSucc n, a => (a ^ n.succ)⁻¹ - zpow_zero' := fun a ↦ by simp - zpow_succ' := fun n a ↦ by simp [pow_succ] - zpow_neg' := fun n a ↦ by simp + zpow_zero' := fun a ↦ by simp only [HPow.hPow, Pow.pow]; simp + zpow_succ' := fun n a ↦ by simp only [HPow.hPow, Pow.pow]; simp [pow_succ] + zpow_neg' := fun n a ↦ rfl /-- Units of a monoid form a group. -/ @[to_additive /-- Additive units of an additive monoid form an additive group. -/] diff --git a/Mathlib/Algebra/Group/WithOne/Basic.lean b/Mathlib/Algebra/Group/WithOne/Basic.lean index c3ce8cae26ec97..165315cf6d0ddc 100644 --- a/Mathlib/Algebra/Group/WithOne/Basic.lean +++ b/Mathlib/Algebra/Group/WithOne/Basic.lean @@ -76,6 +76,11 @@ theorem lift_unique (f : WithOne α →* β) : f = lift (f.toMulHom.comp coeMulH @[to_additive (attr := simp)] theorem lift_symm_apply (f : WithOne α →* β) (x : α) : lift.symm f x = f x := rfl +@[to_additive] +lemma lift_symm_injective_of_injective {f : WithOne α →* β} (hf : Function.Injective f) : + Function.Injective (lift.symm f) := + fun _ _ ↦ by simp [hf.eq_iff] + end lift section Map diff --git a/Mathlib/Algebra/Group/WithOne/Defs.lean b/Mathlib/Algebra/Group/WithOne/Defs.lean index 188f0777955777..b61d5187b8f4a6 100644 --- a/Mathlib/Algebra/Group/WithOne/Defs.lean +++ b/Mathlib/Algebra/Group/WithOne/Defs.lean @@ -125,15 +125,15 @@ lemma recOneCoe_coe {motive : WithOne α → Sort*} (h₁ h₂) (a : α) : rfl /-- Deconstruct an `x : WithOne α` to the underlying value in `α`, given a proof that `x ≠ 1`. -/ -@[to_additive unzero +@[to_additive /-- Deconstruct an `x : WithZero α` to the underlying value in `α`, given a proof that `x ≠ 0`. -/] def unone : ∀ {x : WithOne α}, x ≠ 1 → α | (x : α), _ => x -@[to_additive (attr := simp) unzero_coe] +@[to_additive (attr := simp)] theorem unone_coe {x : α} (hx : (x : WithOne α) ≠ 1) : unone hx = x := rfl -@[to_additive (attr := simp) coe_unzero] +@[to_additive (attr := simp)] lemma coe_unone : ∀ {x : WithOne α} (hx : x ≠ 1), unone hx = x | (x : α), _ => rfl @@ -194,4 +194,36 @@ instance instCommMonoid [CommSemigroup α] : CommMonoid (WithOne α) where theorem coe_inv [Inv α] (a : α) : ((a⁻¹ : α) : WithOne α) = (a : WithOne α)⁻¹ := rfl +/-- +Specialization of `Option.getD` to values in `WithOne α` that respects API boundaries. +-/ +@[to_additive + /-- Specialization of `Option.getD` to values in `WithZero α` that respects API boundaries. -/] +def unoneD (d : α) (x : WithOne α) : α := recOneCoe d id x + +@[to_additive (attr := simp)] +theorem unoneD_one (d : α) : unoneD d 1 = d := + rfl + +@[to_additive (attr := simp)] +theorem unoneD_coe (d x : α) : unoneD d x = x := + rfl + +@[to_additive] +theorem unoneD_eq_iff {d y : α} {x : WithOne α} : unoneD d x = y ↔ x = y ∨ x = 1 ∧ y = d := by + induction x <;> simp [@eq_comm _ d] + +@[to_additive (attr := simp)] +theorem unoneD_eq_self_iff {d : α} {x : WithOne α} : unoneD d x = d ↔ x = d ∨ x = 1 := by + simp [unoneD_eq_iff] + +@[to_additive] +theorem unoneD_eq_unoneD_iff {d : α} {x y : WithOne α} : + unoneD d x = unoneD d y ↔ x = y ∨ x = d ∧ y = 1 ∨ x = 1 ∧ y = d := by + induction y <;> simp [unoneD_eq_iff, or_comm] + +@[to_additive] +lemma unoneD_eq_unone {d : α} {x : WithOne α} (hx : x ≠ 1) : unoneD d x = unone hx := by + simp [unoneD_eq_iff] + end WithOne diff --git a/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean b/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean index 3bf4735533a0d3..90c30e1bd6160f 100644 --- a/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean +++ b/Mathlib/Algebra/GroupWithZero/Action/Opposite.lean @@ -17,7 +17,7 @@ This file defines the actions on the opposite type `SMul R Mᵐᵒᵖ`, and acti type, `SMul Rᵐᵒᵖ M`. Note that `MulOpposite.smul` is provided in an earlier file as it is needed to -provide the `AddMonoid.nsmul` and `AddCommGroup.zsmul` fields. +provide the `NSMul.nsmul` and `ZSMul.zsmul` fields. ## Notation diff --git a/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean b/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean index 618119245f44d2..41a52266808e81 100644 --- a/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean +++ b/Mathlib/Algebra/GroupWithZero/Submonoid/Instances.lean @@ -43,7 +43,7 @@ instance [GroupWithZero G] [GroupWithZero H] (f : G →*₀ H) : obtain ⟨y, hy⟩ := x.prop use y⁻¹ simp [← hy]⟩ - exists_pair_ne := ⟨⟨f 0, 0, rfl⟩, ⟨f 1, by simp [- map_one]⟩, by simp⟩ + exists_pair_ne := ⟨⟨f 0, 0, rfl⟩, ⟨f 1, by simp [-map_one]⟩, by simp⟩ inv_zero := Subtype.ext inv_zero mul_inv_cancel := by rintro ⟨a, ha⟩ h diff --git a/Mathlib/Algebra/GroupWithZero/Units/Basic.lean b/Mathlib/Algebra/GroupWithZero/Units/Basic.lean index 9916a90765c529..eddd2a3619a1e3 100644 --- a/Mathlib/Algebra/GroupWithZero/Units/Basic.lean +++ b/Mathlib/Algebra/GroupWithZero/Units/Basic.lean @@ -170,7 +170,7 @@ theorem Ring.inverse_mul {a b : M₀} (h : IsUnit a ∨ IsUnit b) : (a * b)⁻¹ simp theorem Ring.isUnit_iff_inverse_ne_zero [Nontrivial M₀] {x : M₀} : IsUnit x ↔ x⁻¹ʳ ≠ 0 := - ⟨(IsUnit.ringInverse · |>.ne_zero), by simpa using mt <| Ring.inverse_non_unit (x := x)⟩ + ⟨(IsUnit.ringInverse · |>.ne_zero), by simpa using mt <| Ring.inverse_non_unit (x := x)⟩ grind_pattern Ring.isUnit_iff_inverse_ne_zero => IsUnit x, x⁻¹ʳ diff --git a/Mathlib/Algebra/Homology/Additive.lean b/Mathlib/Algebra/Homology/Additive.lean index 86ab7ca1703cef..56b8a7c83daa15 100644 --- a/Mathlib/Algebra/Homology/Additive.lean +++ b/Mathlib/Algebra/Homology/Additive.lean @@ -141,6 +141,21 @@ instance Functor.mapHomologicalComplex_reflects_iso (F : W₁ ⥤ W₂) [F.Prese haveI := fun n => isIso_of_reflects_iso (f.f n) F exact HomologicalComplex.Hom.isIso_of_components f⟩ +instance (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) [F.Faithful] : + (F.mapHomologicalComplex c).Faithful where + map_injective {K L} f₁ f₂ h := by + ext + exact F.map_injective ((HomologicalComplex.eval W c _).congr_map h) + +instance (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) [F.Faithful] [F.Full] : + (F.mapHomologicalComplex c).Full where + map_surjective {X Y} f := ⟨ + { f n := F.preimage (f.f n) + comm' i j _ := by + apply F.map_injective + simp only [Functor.map_comp, Functor.map_preimage] + exact f.comm i j }, by cat_disch⟩ + variable {W₁} set_option backward.defeqAttrib.useBackward true in @@ -188,6 +203,16 @@ def NatIso.mapHomologicalComplex {F G : W₁ ⥤ W₂} [F.PreservesZeroMorphisms inv_hom_id := by simp only [← NatTrans.mapHomologicalComplex_comp, α.inv_hom_id, NatTrans.mapHomologicalComplex_id] +/-- If additive functors are related by an isomorphism `F ⋙ G ≅ H`, this is +the corresponding isomorphism for the induced functors on categories +of homological complexes. -/ +@[simps!] +def Functor.mapHomologicalComplexCompIso {W' : Type*} [Category W'] [Preadditive W'] + {F : V ⥤ W} {G : W ⥤ W'} {H : V ⥤ W'} (e : F ⋙ G ≅ H) + [F.Additive] [G.Additive] [H.Additive] (c : ComplexShape ι) : + F.mapHomologicalComplex c ⋙ G.mapHomologicalComplex c ≅ H.mapHomologicalComplex c := + NatIso.mapHomologicalComplex e c + set_option backward.defeqAttrib.useBackward true in /-- An equivalence of categories induces an equivalences between the respective categories of homological complex. diff --git a/Mathlib/Algebra/Homology/CochainComplexOpposite.lean b/Mathlib/Algebra/Homology/CochainComplexOpposite.lean index 64b8dbc3f9a1d8..1fa7a1301583ec 100644 --- a/Mathlib/Algebra/Homology/CochainComplexOpposite.lean +++ b/Mathlib/Algebra/Homology/CochainComplexOpposite.lean @@ -199,7 +199,7 @@ lemma exactAt_op {K : CochainComplex C ℤ} {n : ℤ} (hK : K.ExactAt n) (by grind [next])] at hK lemma acyclic_op {K : CochainComplex C ℤ} (hK : K.Acyclic) : - ((opEquivalence C).functor.obj (op K)).Acyclic := + ((opEquivalence C).functor.obj (op K)).Acyclic := fun n ↦ exactAt_op (hK (-n)) n end CochainComplex diff --git a/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean b/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean index 72369bf49d4c35..d78c2f9f096135 100644 --- a/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean +++ b/Mathlib/Algebra/Homology/DerivedCategory/Basic.lean @@ -237,7 +237,7 @@ their compatibilities with shifts. -/ def singleFunctors : SingleFunctors C (DerivedCategory C) ℤ := (HomotopyCategory.singleFunctors C).postcomp Qh -/-- The shift functor `C ⥤ DerivedCategory C` which sends `X : C` to the +/-- The single functor `C ⥤ DerivedCategory C` which sends `X : C` to the single cochain complex with `X` sitting in degree `n : ℤ`. -/ abbrev singleFunctor (n : ℤ) := (singleFunctors C).functor n diff --git a/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean new file mode 100644 index 00000000000000..2699d12c106b74 --- /dev/null +++ b/Mathlib/Algebra/Homology/DerivedCategory/DerivabilityStructureInjectives.lean @@ -0,0 +1,409 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.Algebra.Homology.DerivedCategory.Plus +public import Mathlib.Algebra.Homology.FullSubcategory +public import Mathlib.Algebra.Homology.ModelCategory.Injective +public import Mathlib.AlgebraicTopology.ModelCategory.DerivabilityStructureFibrant +public import Mathlib.CategoryTheory.GuitartExact.Quotient +public import Mathlib.CategoryTheory.Localization.DerivabilityStructure.Derives +public import Mathlib.CategoryTheory.Localization.DerivabilityStructure.OfLocalizedEquivalences +public import Mathlib.CategoryTheory.Preadditive.Injective.InjectiveObject + +/-! +# The injective derivability structure + +Let `C` be an abelian category with enough injectives. +In this file, we define a localizer morphism `CochainComplex.Plus.localizerMorphism` +(relative to quasi-isomorphisms) which is given by the (fully faithful) functor +`CochainComplex.Plus (InjectiveObject C) ⥤ CochainComplex.Plus C`, and we show +that it is a right derivability structure. (The proof proceeds by showing that +up to equivalences of categories, this functor is the inclusion of the full +subcategory of fibrant objects in the model category `CochainComplex.Plus C`.) + +We also obtain a similar right derivability structure `HomotopyCategory.Plus.localizerMorphism` +for the functor `HomotopyCategory.Plus (InjectiveObject C) ⥤ HomotopyCategory.Plus C`, where +the target category is equipped with the class of quasi-isomorphisms while +the source category `HomotopyCategory.Plus (InjectiveObject C)` is equipped +with the class of isomorphisms (which is exactly the same as quasi-isomorphisms). +The consequence is that any functor from the category `HomotopyCategory.Plus C` +has a right derived functor, and we show that the unit natural transformation for +such a derived functor is an isomorphism on objects coming from +`HomotopyCategory.Plus (InjectiveObject C)`. + +-/ + +@[expose] public section + +open HomotopicalAlgebra CategoryTheory Limits + +variable {C H : Type*} [Category* C] [Abelian C] [Category* H] + +namespace CochainComplex.Plus + +instance (X : HomotopyCategory.Plus (InjectiveObject C)) (n : ℤ) : + Injective (((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj X).obj.as.X n) := + inferInstanceAs (Injective ((InjectiveObject.ι C).obj (X.obj.as.X n))) + +set_option backward.defeqAttrib.useBackward true in +instance (K : CochainComplex.Plus (InjectiveObject C)) : + CochainComplex.IsKInjective + (((InjectiveObject.ι C).mapHomologicalComplex (.up ℤ)).obj K.obj) := by + obtain ⟨K, n, hn⟩ := K + let L := ((InjectiveObject.ι C).mapHomologicalComplex (.up ℤ)).obj K + have (n : ℤ) : Injective (L.X n) := by dsimp [L]; infer_instance + exact CochainComplex.isKInjective_of_injective L n + +lemma exists_quasiIso_injective [EnoughInjectives C] + (K : CochainComplex.Plus C) (n : ℤ) [K.obj.IsStrictlyGE n] : + ∃ (L : CochainComplex.Plus (InjectiveObject C)) (_ : L.obj.IsStrictlyGE n) + (i : K ⟶ (InjectiveObject.ι C).mapCochainComplexPlus.obj L), + quasiIso C i := by + obtain ⟨L, i, _, _, _⟩ := modelCategoryQuillen.exists_quasiIso_injective K.obj n + let L' : CochainComplex (InjectiveObject C) ℤ := + HomologicalComplex.liftObjectProperty _ L inferInstance + have hL' : L'.IsStrictlyGE n := by + rwa [← isStrictlyGE_mapHomologicalComplex_obj_iff _ (InjectiveObject.ι _)] + exact ⟨⟨L', n, hL'⟩, hL', ObjectProperty.homMk i, by assumption⟩ + +end CochainComplex.Plus + +namespace DerivedCategory.Plus + +variable [HasDerivedCategory C] + +/-- Let `K` be an object in the bounded below derived category of an abelian category `C` +with enough injectives. Assume that `K` is cohomologically `≥ n`. Then, `K` +admits an "injective resolution", in the sense that there exists a cochain +complex `L` consisting of injective object and lying in degrees `≥ n`, such that `K` +is isomorphic to the image of `L`. -/ +lemma exists_injective_nonempty_iso [EnoughInjectives C] (K : DerivedCategory.Plus C) + (n : ℤ) [K.IsGE n] : + ∃ (L : CochainComplex.Plus (InjectiveObject C)) (_ : L.obj.IsStrictlyGE n), + Nonempty (DerivedCategory.Plus.Q.obj + ((InjectiveObject.ι C).mapCochainComplexPlus.obj L) ≅ K) := by + have : K.obj.IsGE n := (K.isGE_ι_obj_iff n).2 (by assumption) + obtain ⟨L, _, ⟨e⟩⟩ := DerivedCategory.exists_iso_Q_obj_of_isGE K.obj n + obtain ⟨M, _, i, hi⟩ := + CochainComplex.Plus.exists_quasiIso_injective ⟨L, ⟨n, inferInstance⟩⟩ n + have : QuasiIso i.hom := by assumption + exact ⟨M, inferInstance, + ⟨DerivedCategory.Plus.ι.preimageIso ((asIso (DerivedCategory.Q.map i.hom)).symm ≪≫ e.symm)⟩⟩ + +end DerivedCategory.Plus + +namespace CochainComplex.Plus + +variable (C) in +/-- The localizer morphism (relative to quasi-isomorphisms) that is +given by the "inclusion functor" +`CochainComplex.Plus (InjectiveObject C) ⥤ CochainComplex.Plus C`. -/ +@[simps] +def localizerMorphism : + LocalizerMorphism ((quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus) + (quasiIso C) where + functor := (InjectiveObject.ι C).mapCochainComplexPlus + map := by rfl + +instance : (localizerMorphism C).IsInduced where + inverseImage_eq := rfl + +instance (K : Plus (InjectiveObject C)) (n : ℤ) : + Injective (K.obj.X n).obj := + (K.obj.X n).property + +variable [EnoughInjectives C] + +open modelCategoryQuillen + +instance (K : FibrantObject (Plus C)) (n : ℤ) : + Injective (K.obj.obj.X n) := by + obtain ⟨K, hK⟩ := K + rw [fibrantObjects, modelCategoryQuillen.isFibrant_iff] at hK + infer_instance + +variable (C) in +set_option backward.defeqAttrib.useBackward true in +/-- The equivalence between `CochainComplex.Plus (InjectiveObject C)` +and the category of fibrant object in `CochainComplex.Plus C` for the +Quillen model category structure. -/ +def fibrantObjectEquivalence : + Plus (InjectiveObject C) ≌ FibrantObject (Plus C) where + functor := ObjectProperty.lift _ (InjectiveObject.ι C).mapCochainComplexPlus (fun K ↦ by + dsimp [fibrantObjects] + rw [modelCategoryQuillen.isFibrant_iff] + intro n + dsimp + infer_instance) + inverse := ObjectProperty.lift _ + (HomologicalComplex.liftFunctorObjectProperty _ (FibrantObject.ι ⋙ Plus.ι C) + (fun K n ↦ by dsimp; infer_instance)) (by + rintro ⟨⟨_, n, _⟩, _⟩ + refine ⟨n, ?_⟩ + rwa [← isStrictlyGE_mapHomologicalComplex_obj_iff _ (InjectiveObject.ι _)]) + unitIso := Iso.refl _ + counitIso := Iso.refl _ + +variable (C) in +/-- The localizer morphism (relative to quasi-isomorphisms) that is +given by the equivalence of categories +`CochainComplex.Plus (InjectiveObject C) ≌ FibrantObject (CochainComplex.Plus C)`. -/ +abbrev fibrantObjectLocalizerMorphism : + LocalizerMorphism ((quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus) + (weakEquivalences (FibrantObject (Plus C))) where + functor := (fibrantObjectEquivalence C).functor + map := by rfl + +instance : (fibrantObjectLocalizerMorphism C).IsInduced where + inverseImage_eq := rfl + +set_option backward.isDefEq.respectTransparency false in +instance : (localizerMorphism C).IsRightDerivabilityStructure := by + rw [LocalizerMorphism.isRightDerivabilityStructure_iff_of_equivalences + (T := localizerMorphism C) (B := FibrantObject.localizerMorphism (Plus C)) + (R := .id _) (L := fibrantObjectLocalizerMorphism C) (Iso.refl _)] + infer_instance + +set_option backward.isDefEq.respectTransparency false in +instance : (localizerMorphism C).arrow.HasRightResolutions := by + rw [LocalizerMorphism.hasRightResolutions_arrow_iff_of_equivalences + (T := localizerMorphism C) (B := FibrantObject.localizerMorphism (Plus C)) + (R := .id _) (L := fibrantObjectLocalizerMorphism C) (Iso.refl _)] + infer_instance + +end CochainComplex.Plus + +namespace HomotopyCategory.Plus + +variable (C) in +/-- The localizer morphism that is given by the "inclusion functor" +`HomotopyCategory.Plus (InjectiveObject C) ⥤ HomotopyCategory.Plus C`. +The target category is equipped with the class of quasi-isomorphisms while +the source category `HomotopyCategory.Plus (InjectiveObject C)` is equipped +with the class of isomorphisms (which is exactly the same as quasi-isomorphisms). -/ +abbrev localizerMorphism : LocalizerMorphism + (MorphismProperty.isomorphisms (HomotopyCategory.Plus (InjectiveObject C))) + (HomotopyCategory.Plus.quasiIso C) where + functor := (InjectiveObject.ι C).mapHomotopyCategoryPlus + map K L f (hf : IsIso f) := by + dsimp only [MorphismProperty.inverseImage, HomotopyCategory.Plus.quasiIso] + rw [HomotopyCategory.mem_quasiIso_iff] + intro n + infer_instance + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +lemma isIso_quotient_map_iff + {K L : CochainComplex.Plus (InjectiveObject C)} (f : K ⟶ L) : + IsIso ((quotient _).map f) ↔ + CochainComplex.Plus.quasiIso C ((InjectiveObject.ι C).mapCochainComplexPlus.map f) := by + rw [← isIso_iff_of_reflects_iso _ (HomotopyCategory.Plus.ι (InjectiveObject C)), + ← isIso_iff_of_reflects_iso _ (Functor.mapHomotopyCategory (InjectiveObject.ι C) (.up ℤ))] + dsimp + rw [HomologicalComplex.isIso_quotient_map_iff_homotopyEquivalences, + ← CochainComplex.IsKInjective.quasiIso_iff] + rfl + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +open HomologicalComplex in +lemma inverseImage_quasiIso_mapCochainComplexPlus_injectiveObjectι : + (CochainComplex.Plus.quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus = + (homotopyEquivalences (InjectiveObject C) (.up ℤ)).inverseImage + (CochainComplex.Plus.ι (InjectiveObject C)) := by + ext K L f + simp [CochainComplex.Plus.quasiIso, Functor.mapCochainComplexPlus, + ← HomologicalComplex.isIso_quotient_map_iff_homotopyEquivalences, + CochainComplex.IsKInjective.quasiIso_iff, + ← isIso_iff_of_reflects_iso _ ((InjectiveObject.ι C).mapHomotopyCategory (.up ℤ))] + +instance : + (HomotopyCategory.Plus.quotient (InjectiveObject C)).IsLocalization + ((CochainComplex.Plus.quasiIso C).inverseImage + (InjectiveObject.ι C).mapCochainComplexPlus) := by + rw [inverseImage_quasiIso_mapCochainComplexPlus_injectiveObjectι] + infer_instance + +set_option backward.isDefEq.respectTransparency false in +open HomologicalComplex in +instance (L : Plus C ⥤ H) [L.IsLocalization (quasiIso C)] : + (quotient C ⋙ L).IsLocalization (CochainComplex.Plus.quasiIso C) := by + refine Functor.IsLocalization.comp _ _ + ((homotopyEquivalences C (.up ℤ)).inverseImage (CochainComplex.Plus.ι C)) + (quasiIso C) _ ?_ ?_ ?_ + · intro _ _ f hf + refine Localization.inverts L (quasiIso C) _ ?_ + simpa [quasiIso, quotient_map_mem_quasiIso_iff] + · intro K L f hf + exact homotopyEquivalences_le_quasiIso _ _ _ hf + · rintro K L f hf + obtain ⟨K, rfl⟩ := Plus.quotient_obj_surjective K + obtain ⟨L, rfl⟩ := Plus.quotient_obj_surjective L + obtain ⟨f, rfl⟩ := (Plus.quotient C).map_surjective f + apply MorphismProperty.map_mem_map + simpa [quasiIso, quotient_map_mem_quasiIso_iff] using! hf + +namespace isRightDerivabilityStructure + +/-! The following private definitions are used to deduce that +`HomotopyCategory.Plus.localizerMorphism` is a right derivability structure +from the fact that `CochainComplex.Plus.localizerMorphism` is. + +The strategy is to observe that the following commutative square +of localizer morphisms gives a Guitart exact square: +``` + CochainComplex.Plus.localizerMorphism C +CochainComplex.Plus (InjectiveObject C) ----------> CochainComplex.Plus C + | | + L C | | R C + v v +HomotopyCategory.Plus (InjectiveObject C) --------> HomotopyCategory.Plus C + HomotopyCategory.Plus.localizerMorphism C +``` +That the square is Guitart exact will follow from the lemma +`TwoSquare.GuitartExact.quotient_of_nonempty_rightHomotopy` +from the file `Mathlib/CategoryTheory/GuitartExact/Quotient.lean`. + +-/ + +open MorphismProperty + +variable (C) in +/-- The left localizer morphism in the Guitart exact square `iso`. -/ +private abbrev L : LocalizerMorphism + ((CochainComplex.Plus.quasiIso C).inverseImage (InjectiveObject.ι C).mapCochainComplexPlus) + (isomorphisms (Plus (InjectiveObject C))) where + functor := HomotopyCategory.Plus.quotient (InjectiveObject C) + map _ _ f hf := (isIso_quotient_map_iff f).2 hf + +private instance : (L C).IsInduced where + inverseImage_eq := by ext; apply isIso_quotient_map_iff + +variable (C) in +set_option backward.isDefEq.respectTransparency false in +/-- The right localizer morphism in the Guitart exact square `iso`. -/ +private abbrev R : LocalizerMorphism (CochainComplex.Plus.quasiIso C) (quasiIso C) where + functor := HomotopyCategory.Plus.quotient C + map _ _ _ _ := by simpa [quasiIso, quotient_map_mem_quasiIso_iff] + +private instance : (R C).IsInduced where + inverseImage_eq := by ext; apply quotient_map_mem_quasiIso_iff + +private instance : (L C).IsLocalizedEquivalence := by + have : + ((L C).functor ⋙ 𝟭 (Plus (InjectiveObject C))).IsLocalization + ((CochainComplex.Plus.quasiIso C).inverseImage + (InjectiveObject.ι C).mapCochainComplexPlus) := + inferInstanceAs ((HomotopyCategory.Plus.quotient (InjectiveObject C)).IsLocalization _) + exact LocalizerMorphism.IsLocalizedEquivalence.of_isLocalization_of_isLocalization (L C) (𝟭 _) + +private instance : (R C).IsLocalizedEquivalence := + LocalizerMorphism.IsLocalizedEquivalence.of_isLocalization_of_isLocalization + (R C) ((quasiIso C).Q) + +variable (C) in +/-- The "commutative" square of functors involving the underlying functors +of the localizer morphisms `CochainComplex.Plus.localizerMorphism C` +and `HomotopyCategory.Plus.localizerMorphism C`. -/ +private def iso : + (CochainComplex.Plus.localizerMorphism C).functor ⋙ (R C).functor ≅ + (L C).functor ⋙ (localizerMorphism C).functor := Iso.refl _ + +set_option backward.defeqAttrib.useBackward true in +open HomologicalComplex CochainComplex in +private instance : TwoSquare.GuitartExact (iso C).hom := + TwoSquare.GuitartExact.quotient_of_nonempty_rightHomotopy (iso C).symm (by + rintro ⟨K₁, n₁, hn₁⟩ ⟨K₂, n₂, hn₂⟩ f₀ f₁ hf + obtain ⟨f₀, rfl⟩ := ObjectProperty.homMk_surjective f₀ + obtain ⟨f₁, rfl⟩ := ObjectProperty.homMk_surjective f₁ + dsimp [Functor.mapCochainComplexPlus] at f₀ f₁ + refine ⟨Plus.prepathObject _, ?_, ⟨?_⟩⟩ + · ext : 1 + exact eq_of_homotopy _ _ (pathObject.homotopy₀₁ _ (fun n ↦ ⟨n + 1, by simp⟩)) + · refine PrepathObject.RightHomotopy.fullSubcategoryEquiv.symm + { h := pathObject.lift f₀ f₁ (homotopyOfEq _ _ + ((HomotopyCategory.Plus.ι C).congr_map hf)) ≫ + (pathObject.mapHomologicalComplexObjIso K₂ (InjectiveObject.ι C) + (fun n ↦ ⟨n + 1, by simp⟩)).inv + h₀ := ?_ + h₁ := ?_ } + all_goals + dsimp [Functor.mapCochainComplexPlus] + cat_disch) + +end isRightDerivabilityStructure + +variable [EnoughInjectives C] + +instance isRightDerivabilityStructure : (localizerMorphism C).IsRightDerivabilityStructure := + LocalizerMorphism.isRightDerivabilityStructure_of_isLocalizedEquivalence + (isRightDerivabilityStructure.iso C) + +instance : (HomotopyCategory.Plus.localizerMorphism C).arrow.HasRightResolutions := + LocalizerMorphism.hasRightResolutions_arrow_of_essSurj_of_full + (isRightDerivabilityStructure.iso C) + +instance [HasDerivedCategory C] : + ((InjectiveObject.ι C).mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh).EssSurj where + mem_essImage K := by + let r : (HomotopyCategory.Plus.localizerMorphism C).RightResolution + (DerivedCategory.Plus.Qh.objPreimage K) := Classical.arbitrary _ + have := Localization.inverts DerivedCategory.Plus.Qh _ _ r.hw + exact ⟨r.X₁, ⟨(asIso (DerivedCategory.Plus.Qh.map r.w)).symm ≪≫ + DerivedCategory.Plus.Qh.objObjPreimageIso K⟩⟩ + +section + +variable (F : HomotopyCategory.Plus C ⥤ H) + +omit [EnoughInjectives C] in +lemma localizerMorphism_derives : (localizerMorphism C).Derives F := + MorphismProperty.isInvertedBy_isomorphisms _ + +/-- Any functor from the bounded below homotopy category has a right derived functor +with respect to quasi-isomorphisms. -/ +instance : F.HasPointwiseRightDerivedFunctor (HomotopyCategory.Plus.quasiIso C) := + (localizerMorphism_derives F).hasPointwiseRightDerivedFunctor + +variable [HasDerivedCategory C] (F' : DerivedCategory.Plus C ⥤ H) + (α : F ⟶ DerivedCategory.Plus.Qh ⋙ F') + [F'.IsRightDerivedFunctor α (HomotopyCategory.Plus.quasiIso C)] + +instance (K : HomotopyCategory.Plus C) [∀ (n : ℤ), Injective (K.obj.as.X n)] : + IsIso (α.app K) := by + have (Y : HomotopyCategory.Plus (InjectiveObject C)) : + IsIso (α.app ((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj Y)) := + (localizerMorphism_derives F).isIso_of_isRightDerivedFunctor _ _ + obtain ⟨Y, ⟨e⟩⟩ : (InjectiveObject.ι C).mapHomotopyCategoryPlus.essImage K := by + obtain ⟨X, hX⟩ := K + obtain ⟨K, rfl⟩ := HomotopyCategory.quotient_obj_surjective X + refine ⟨(quotient _).obj + ((CochainComplex.Plus.fibrantObjectEquivalence C).inverse.obj + ⟨⟨K, by simpa using hX⟩, ?_⟩), ⟨Iso.refl _⟩⟩ + dsimp [fibrantObjects] + rwa [CochainComplex.Plus.modelCategoryQuillen.isFibrant_iff] + rw [← NatTrans.isIso_app_iff_of_iso α e] + infer_instance + +instance (K : CochainComplex.Plus C) (n : ℤ) [Injective (K.obj.X n)] : + Injective (((HomotopyCategory.Plus.quotient C).obj K).obj.as.X n) := by + assumption + +instance (K : CochainComplex.Plus (InjectiveObject C)) (n : ℤ) : + Injective (((HomotopyCategory.Plus.quotient C).obj + ((InjectiveObject.ι C).mapCochainComplexPlus.obj K)).obj.as.X n) := + (K.obj.X n).property + +example (X : HomotopyCategory.Plus (InjectiveObject C)) : + IsIso ((F.totalRightDerivedUnit DerivedCategory.Plus.Qh + (HomotopyCategory.Plus.quasiIso C)).app + ((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj X)) := by + infer_instance + +end + +end HomotopyCategory.Plus diff --git a/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean b/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean index 8ca61a1c2e2c22..0a5406598d9864 100644 --- a/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean +++ b/Mathlib/Algebra/Homology/DerivedCategory/HomologySequence.lean @@ -85,6 +85,22 @@ lemma isIso_Qh_map_iff {X Y : HomotopyCategory C (ComplexShape.up ℤ)} (f : X infer_instance · exact Localization.inverts Qh (HomotopyCategory.quasiIso _ _) _ +lemma isIso_iff {K L : DerivedCategory C} (f : K ⟶ L) : + IsIso f ↔ ∀ (n : ℤ), IsIso ((homologyFunctor C n).map f) := by + refine ⟨fun hf n ↦ inferInstance, fun hf ↦ ?_⟩ + refine ((MorphismProperty.isomorphisms (DerivedCategory C)).arrow_iso_iff + (Qh.mapArrow.objObjPreimageIso (Arrow.mk f))).1 ?_ + let g := Qh.mapArrow.objPreimage (Arrow.mk f) + change IsIso (Qh.map g.hom) + rw [isIso_Qh_map_iff, HomotopyCategory.mem_quasiIso_iff] + intro n + have e : Arrow.mk ((homologyFunctor C n).map f) ≅ + Arrow.mk ((HomotopyCategory.homologyFunctor _ _ n).map g.hom) := + ((homologyFunctor C n).mapArrow.mapIso + ((Qh.mapArrow.objObjPreimageIso (Arrow.mk f)).symm)) ≪≫ + ((Functor.mapArrowFunctor _ _).mapIso (homologyFunctorFactorsh C n)).app (Arrow.mk g.hom) + exact ((MorphismProperty.isomorphisms C).arrow_iso_iff e).1 (hf n) + instance (n : ℤ) : (homologyFunctor C n).IsHomological := Functor.isHomological_of_localization Qh (homologyFunctor C n) _ (homologyFunctorFactorsh C n) diff --git a/Mathlib/Algebra/Homology/DerivedCategory/Plus.lean b/Mathlib/Algebra/Homology/DerivedCategory/Plus.lean new file mode 100644 index 00000000000000..72b30ddd52edf2 --- /dev/null +++ b/Mathlib/Algebra/Homology/DerivedCategory/Plus.lean @@ -0,0 +1,228 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.Algebra.Homology.DerivedCategory.KInjective +public import Mathlib.Algebra.Homology.DerivedCategory.TStructure +public import Mathlib.Algebra.Homology.HomotopyCategory.Plus +public import Mathlib.CategoryTheory.Triangulated.LocalizingSubcategory +public import Mathlib.CategoryTheory.Triangulated.TStructure.Induced + +/-! +# The bounded below derived category + +Let `C` be an abelian category. In this file, we show that +the bounded below derived category `DerivedCategory.Plus C` (defined +as a full subcategory of `DerivedCategory C`) is the localization +of the bounded below homotopy category `HomotopyCategory.Plus C` +with respect to quasi-isomorphisms. + +-/ + +@[expose] public section + +open CategoryTheory Category Triangulated Limits + +variable {C : Type*} [Category* C] [Abelian C] + +namespace HomotopyCategory.Plus + +variable (C) + +/-- The property of objects in `HomotopyCategory.Plus C` that is satisfied +by acyclic complexes. -/ +abbrev subcategoryAcyclic : + ObjectProperty (HomotopyCategory.Plus C) := + (HomotopyCategory.subcategoryAcyclic C).inverseImage (HomotopyCategory.Plus.ι C) + +set_option backward.defeqAttrib.useBackward true in +lemma quasiIso_eq_subcategoryAcyclic_trW : + HomotopyCategory.Plus.quasiIso C = (subcategoryAcyclic C).trW := by + ext K L f + obtain ⟨M, g, h, mem⟩ := CategoryTheory.Pretriangulated.distinguished_cocone_triangle f + have := (HomotopyCategory.subcategoryAcyclic C).trW_iff_of_distinguished _ + ((HomotopyCategory.Plus.ι C).map_distinguished _ mem) + rw [← HomotopyCategory.quasiIso_eq_trW_subcategoryAcyclic] at this + rwa [dsimp% (subcategoryAcyclic C).trW_iff_of_distinguished _ mem] + +end HomotopyCategory.Plus + +namespace DerivedCategory + +open TStructure + +variable [HasDerivedCategory C] + +namespace Plus + +/-- The localization functor `HomotopyCategory.Plus C ⥤ DerivedCategory.Plus C`. -/ +noncomputable def Qh : HomotopyCategory.Plus C ⥤ Plus C := + t.plus.lift (HomotopyCategory.Plus.ι _ ⋙ DerivedCategory.Qh) (by + rintro ⟨K, hK⟩ + obtain ⟨K, rfl⟩ := HomotopyCategory.quotient_obj_surjective K + obtain ⟨n, _⟩ := (HomotopyCategory.plus_quotient_obj_iff _).mp hK + exact ⟨n, t.isGE_of_iso ((quotientCompQhIso C).symm.app K) n⟩) + +noncomputable instance : (Qh : _ ⥤ Plus C).CommShift ℤ := by + dsimp only [Qh] + infer_instance + +set_option backward.isDefEq.respectTransparency false in +instance : (Qh : _ ⥤ Plus C).IsTriangulated := by + dsimp only [Qh] + infer_instance + +set_option backward.isDefEq.respectTransparency false in +lemma Qh_map_bijective_of_isKInjective (K L : HomotopyCategory.Plus C) + (_ : CochainComplex.IsKInjective L.1.as) : Function.Bijective (Qh.map : (K ⟶ L) → _) := by + have := CochainComplex.IsKInjective.Qh_map_bijective K.1 L.1.as + rw [← Function.Bijective.of_comp_iff _ + ((HomotopyCategory.Plus.fullyFaithfulι C).map_bijective _ _)] at this + rwa [← Function.Bijective.of_comp_iff' (t.plus.fullyFaithfulι.map_bijective _ _)] + +instance : (HomotopyCategory.plus C).IsVerdierRightLocalizing + (HomotopyCategory.subcategoryAcyclic C) where + fac {K L} φ hK hL := by + obtain ⟨K : CochainComplex _ _, rfl⟩ := HomotopyCategory.quotient_obj_surjective K + obtain ⟨L : CochainComplex _ _, rfl⟩ := HomotopyCategory.quotient_obj_surjective L + simp only [HomotopyCategory.plus_quotient_obj_iff] at hL + obtain ⟨n, hn⟩ := hL + obtain ⟨φ, rfl⟩ := (HomotopyCategory.quotient _ _).map_surjective φ + rw [HomotopyCategory.quotient_obj_mem_subcategoryAcyclic_iff_acyclic] at hK + refine ⟨(HomotopyCategory.quotient _ _).obj (K.truncGE n), + (HomotopyCategory.quotient _ _).map (K.πTruncGE n), + (HomotopyCategory.quotient _ _).map (CochainComplex.truncGEMap φ n ≫ inv (L.πTruncGE n)), + ?_, ?_, by simp [← Functor.map_comp]⟩ + · simp only [HomotopyCategory.plus_quotient_obj_iff] + exact ⟨n, inferInstance⟩ + · rw [HomotopyCategory.quotient_obj_mem_subcategoryAcyclic_iff_acyclic] + exact hK.truncGE _ + +variable (C) + +/-- The functor `DerivedCategory.Plus.Qh : HomotopyCategory.Plus C ⥤ DerivedCategory.Plus C` +is induced by `DerivedCategory.Qh : HomotopyCategory C (.up ℤ) ⥤ DerivedCategory C`. -/ +noncomputable def QhCompιIsoιCompQh : + Qh ⋙ Plus.ι ≅ HomotopyCategory.Plus.ι C ⋙ DerivedCategory.Qh := Iso.refl _ + +instance : (Qh (C := C)).EssSurj where + mem_essImage := by + intro ⟨X, n, K, e, h⟩ + refine ⟨⟨(HomotopyCategory.quotient C (ComplexShape.up ℤ)).obj K, ?_⟩, + ⟨Plus.ι.preimageIso ((quotientCompQhIso C).app _ ≪≫ e.symm)⟩⟩ + simp only [HomotopyCategory.plus_quotient_obj_iff] + exact ⟨n, h⟩ + +instance : Qh.IsLocalization (HomotopyCategory.Plus.subcategoryAcyclic C).trW := + ((HomotopyCategory.plus C).triangulatedLocalizerMorphism + (HomotopyCategory.subcategoryAcyclic C)).isLocalization_of_isLocalizedFullyFaithful + (QhCompιIsoιCompQh C).symm + +instance : Qh.IsLocalization (HomotopyCategory.Plus.quasiIso C) := by + rw [HomotopyCategory.Plus.quasiIso_eq_subcategoryAcyclic_trW] + infer_instance + +/-- The single functors `C ⥤ DerivedCategory.Plus C` for all `n : ℤ` along with +their compatibilities with shifts. -/ +noncomputable def singleFunctors : SingleFunctors C (Plus C) ℤ := + SingleFunctors.lift (DerivedCategory.singleFunctors C) Plus.ι + (fun n => t.plus.lift (DerivedCategory.singleFunctor C n) + (fun _ => ⟨n, inferInstance⟩)) + (fun _ => Iso.refl _) + +/-- The single functor `C ⥤ DerivedCategory.Plus C` which sends `X : C` to the +single cochain complex with `X` sitting in degree `n : ℤ`. -/ +noncomputable abbrev singleFunctor (n : ℤ) : C ⥤ Plus C := (singleFunctors C).functor n + +/-- The single functors on `DerivedCategory.Plus C` are induced by the +single functors on `DerivedCategory C`. -/ +noncomputable def singleFunctorιIso (n : ℤ) : + singleFunctor C n ⋙ Plus.ι ≅ DerivedCategory.singleFunctor C n := + Iso.refl _ + +instance (n : ℤ) : (singleFunctor C n).Additive := by + dsimp [singleFunctor, singleFunctors] + infer_instance + +/-- The homology functor `DerivedCategory.Plus C ⥤ C` in degree `n : ℤ`. -/ +noncomputable def homologyFunctor (n : ℤ) : Plus C ⥤ C := + Plus.ι ⋙ DerivedCategory.homologyFunctor C n +deriving Functor.IsHomological + +instance : (Qh (C := C)).mapArrow.EssSurj := + Localization.essSurj_mapArrow _ + (HomotopyCategory.Plus.subcategoryAcyclic C).trW + +variable {C} + +/-- The canonical t-structure on `DerivedCategory.Plus C`. -/ +noncomputable abbrev TStructure.t : TStructure (DerivedCategory.Plus C) := + (DerivedCategory.TStructure.t (C := C)).plus.tStructure DerivedCategory.TStructure.t + +/-- Given `X : DerivedCategory.Plus C` and `n : ℤ`, this property means +that `X` is `≥ n` for the canonical t-structure. -/ +abbrev IsGE (X : Plus C) (n : ℤ) : Prop := Plus.TStructure.t.IsGE X n + +/-- Given `X : DerivedCategory.Plus C` and `n : ℤ`, this property means +that `X` is `≤ n` for the canonical t-structure. -/ +abbrev IsLE (X : Plus C) (n : ℤ) : Prop := Plus.TStructure.t.IsLE X n + +lemma isGE_ι_obj_iff (X : Plus C) (n : ℤ) : + (ι.obj X).IsGE n ↔ X.IsGE n := by + constructor + all_goals exact fun h ↦ ⟨h.1⟩ + +lemma isLE_ι_obj_iff (X : Plus C) (n : ℤ) : + (ι.obj X).IsLE n ↔ X.IsLE n := by + constructor + all_goals exact fun h ↦ ⟨h.1⟩ + +instance (X : Plus C) (n : ℤ) [X.IsGE n] : (ι.obj X).IsGE n := by + rw [isGE_ι_obj_iff] + infer_instance + +instance (X : Plus C) (n : ℤ) [X.IsLE n] : (ι.obj X).IsLE n := by + rw [isLE_ι_obj_iff] + infer_instance + +noncomputable instance : (DerivedCategory.Plus.homologyFunctor C 0).ShiftSequence ℤ := + inferInstanceAs ((ι ⋙ DerivedCategory.homologyFunctor C 0).ShiftSequence ℤ) + +instance (X : C) (n : ℤ) : ((singleFunctor C n).obj X).IsGE n := by + rw [← isGE_ι_obj_iff] + change DerivedCategory.TStructure.t.IsGE ((DerivedCategory.singleFunctor C n).obj X) n + infer_instance + +instance (X : C) (n : ℤ) : ((singleFunctor C n).obj X).IsLE n := by + rw [← isLE_ι_obj_iff] + change DerivedCategory.TStructure.t.IsLE ((DerivedCategory.singleFunctor C n).obj X) n + infer_instance + +lemma isZero_homology_of_isGE + (X : Plus C) (n : ℤ) [X.IsGE n] (i : ℤ) (hi : i < n) : + IsZero ((homologyFunctor C i).obj X) := + (ι.obj X).isZero_of_isGE n i hi + +lemma isZero_homology_of_isLE + (X : Plus C) (n : ℤ) [X.IsLE n] (i : ℤ) (hi : n < i) : + IsZero ((homologyFunctor C i).obj X) := + (ι.obj X).isZero_of_isLE n i hi + +lemma isIso_iff {X Y : Plus C} (f : X ⟶ Y) : + IsIso f ↔ ∀ (n : ℤ), IsIso ((homologyFunctor C n).map f) := by + refine ⟨fun _ _ ↦ inferInstance, fun _ ↦ ?_⟩ + have : IsIso (ι.map f) := by rwa [DerivedCategory.isIso_iff] + exact isIso_of_fully_faithful ι _ + +/-- The localization functor `CochainComplex.Plus C ⥤ DerivedCategory.Plus C`. -/ +noncomputable def Q : CochainComplex.Plus C ⥤ DerivedCategory.Plus C := + HomotopyCategory.Plus.quotient C ⋙ Qh + +-- TODO: show that `Q` is indeed a localization functor with respect to quasi-isomorphisms + +end Plus + +end DerivedCategory diff --git a/Mathlib/Algebra/Homology/DerivedCategory/RightDerivedFunctorPlus.lean b/Mathlib/Algebra/Homology/DerivedCategory/RightDerivedFunctorPlus.lean new file mode 100644 index 00000000000000..1fbd95aa538737 --- /dev/null +++ b/Mathlib/Algebra/Homology/DerivedCategory/RightDerivedFunctorPlus.lean @@ -0,0 +1,70 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.Algebra.Homology.DerivedCategory.DerivabilityStructureInjectives + +/-! +# The right derived functor on the bounded below derived category + +If `F : C ⥤ D` is an additive functor between abelian categories, +where `C` has enough injectives, we define the right derived functor +`F.rightDerivedFunctorPlus : DerivedCategory.Plus C ⥤ DerivedCategory.Plus D` +between the corresponding bounded below derived categories. + +TODO(@joelriou): show that this functor is triangulated and refactor +the definiton of `Functor.rightDerived` + +-/ + +@[expose] public section + +namespace CategoryTheory + +namespace Functor + +variable {C D : Type*} [Category* C] [Category* D] [Abelian C] [Abelian D] + [HasDerivedCategory C] [HasDerivedCategory D] + (F : C ⥤ D) [F.Additive] [EnoughInjectives C] + +/-- The right derived functor `DerivedCategory.Plus C ⥤ DerivedCategory.Plus D` +when `F : C ⥤ D` is an additive functor between abelian categories and +`C` has enough injectives. -/ +noncomputable def rightDerivedFunctorPlus : + DerivedCategory.Plus C ⥤ DerivedCategory.Plus D := + (F.mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh).totalRightDerived DerivedCategory.Plus.Qh + (HomotopyCategory.Plus.quasiIso C) + +/-- The natural transformation that is part of the data of +the right derived functor `DerivedCategory.Plus C ⥤ DerivedCategory.Plus D` +when `F : C ⥤ D` is an additive functor between abelian categories and +`C` has enough injectives. -/ +noncomputable def rightDerivedFunctorPlusUnit : + F.mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh ⟶ + DerivedCategory.Plus.Qh ⋙ F.rightDerivedFunctorPlus := + (F.mapHomotopyCategoryPlus ⋙ DerivedCategory.Plus.Qh).totalRightDerivedUnit + DerivedCategory.Plus.Qh (HomotopyCategory.Plus.quasiIso C) + +instance : + F.rightDerivedFunctorPlus.IsRightDerivedFunctor + F.rightDerivedFunctorPlusUnit (HomotopyCategory.Plus.quasiIso C) := by + dsimp only [rightDerivedFunctorPlus, rightDerivedFunctorPlusUnit] + infer_instance + +example (X : HomotopyCategory.Plus (InjectiveObject C)) : + IsIso (F.rightDerivedFunctorPlusUnit.app + ((InjectiveObject.ι C).mapHomotopyCategoryPlus.obj X)) := by + infer_instance + +example (K : CochainComplex.Plus (InjectiveObject C)) : + IsIso (F.rightDerivedFunctorPlusUnit.app + ((HomotopyCategory.Plus.quotient C).obj + ((InjectiveObject.ι C).mapCochainComplexPlus.obj K))) := by + infer_instance + +end Functor + +end CategoryTheory diff --git a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean index ae92cb4c519546..1cc81f41cfa053 100644 --- a/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean +++ b/Mathlib/Algebra/Homology/DerivedCategory/TStructure.lean @@ -76,7 +76,7 @@ noncomputable def TStructure.t : TStructure (DerivedCategory C) where rw [id_comp] rfl · dsimp - rw [← Q.map_comp, CochainComplex.g_shortComplexTruncLEX₃ToTruncGE, + rw [← Q.map_comp, CochainComplex.g_shortComplexTruncLEX₃ToTruncGE .., Iso.hom_inv_id_assoc] /-- Given `X : DerivedCategory C` and `n : ℤ`, this property means @@ -193,4 +193,28 @@ lemma exists_iso_singleFunctor_obj_of_isGE_of_isLE obtain ⟨Y, ⟨e'⟩⟩ := CochainComplex.exists_iso_single K n exact ⟨Y, ⟨e ≪≫ Q.mapIso e'⟩⟩ +open DerivedCategory.TStructure + +variable (C) + +/-- The bounded above derived category of an abelian category. -/ +abbrev Minus : Type max u v := (t : TStructure (DerivedCategory C)).minus.FullSubcategory + +/-- The bounded below derived category of an abelian category. -/ +abbrev Plus : Type max u v := (t : TStructure (DerivedCategory C)).plus.FullSubcategory + +/-- The bounded derived category of an abelian category. -/ +abbrev Bounded : Type max u v := (t : TStructure (DerivedCategory C)).bounded.FullSubcategory + +variable {C} + +/-- The inclusion of the bounded above derived category. -/ +noncomputable abbrev Minus.ι : Minus C ⥤ DerivedCategory C := t.minus.ι + +/-- The inclusion of the bounded below derived category. -/ +noncomputable abbrev Plus.ι : Plus C ⥤ DerivedCategory C := t.plus.ι + +/-- The inclusion of the bounded derived category. -/ +noncomputable abbrev Bounded.ι : Bounded C ⥤ DerivedCategory C := t.bounded.ι + end DerivedCategory diff --git a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean index cd3908b34d4fc7..ad35d9c62b186f 100644 --- a/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean +++ b/Mathlib/Algebra/Homology/Embedding/CochainComplex.lean @@ -313,6 +313,24 @@ lemma quasiIso_truncLEMap_iff : end +section + +variable {D : Type*} [Category* D] [HasZeroMorphisms D] + +lemma isStrictlyGE_mapHomologicalComplex_obj_iff + (F : C ⥤ D) [F.Faithful] [F.PreservesZeroMorphisms] (n : ℤ) : + CochainComplex.IsStrictlyGE ((F.mapHomologicalComplex (.up ℤ)).obj K) n ↔ + K.IsStrictlyGE n := + isStrictlySupported_mapHomologicalComplex_obj_iff .. + +lemma isStrictlyLE_mapHomologicalComplex_obj_iff + (F : C ⥤ D) [F.Faithful] [F.PreservesZeroMorphisms] (n : ℤ) : + CochainComplex.IsStrictlyLE ((F.mapHomologicalComplex (.up ℤ)).obj K) n ↔ + K.IsStrictlyLE n := + isStrictlySupported_mapHomologicalComplex_obj_iff .. + +end + end HasZeroMorphisms section Preadditive @@ -361,6 +379,63 @@ end end Preadditive +section HasZeroMorphisms + +variable {C : Type*} [Category C] [HasZeroMorphisms C] [HasZeroObject C] + (K L : CochainComplex C ℤ) (φ : K ⟶ L) (e : K ≅ L) + [∀ (i : ℤ), K.HasHomology i] [∀ (i : ℤ), L.HasHomology i] (n : ℤ) + +set_option backward.defeqAttrib.useBackward true in +/-- When `K` is a cochain complex indexed by `ℤ` and `n < i`, this is +the isomorphism `(K.truncGE n).X i ≅ K.X i`. -/ +noncomputable def truncGEXIso (n i : ℤ) (hi : n < i := by lia) : + (K.truncGE n).X i ≅ K.X i := + HomologicalComplex.truncGEXIso K (embeddingUpIntGE n) (i := (i - n).natAbs) (by + dsimp + rw [Int.natAbs_of_nonneg (by lia), add_sub_cancel]) + (fun h ↦ by + rw [boundaryGE_embeddingUpIntGE_iff, Int.natAbs_eq_zero] at h + lia) + +set_option backward.defeqAttrib.useBackward true in +/-- When `K` is a cochain complex indexed by `ℤ` and `i < n`, this is +the isomorphism `(K.truncLE n).X i ≅ K.X i`. -/ +noncomputable def truncLEXIso (n i : ℤ) (hi : i < n := by lia) : + (K.truncLE n).X i ≅ K.X i := + HomologicalComplex.truncLEXIso K (embeddingUpIntLE n) (i := (n - i).natAbs) (by + dsimp + rw [Int.natAbs_of_nonneg (by lia), sub_sub_cancel]) + (fun h ↦ by + rw [boundaryLE_embeddingUpIntLE_iff, Int.natAbs_eq_zero] at h + lia) + +/-- When `K` is a cochain complex indexed by `ℤ`, this is the isomorphism +`(K.truncGE n).X n ≅ K.opcycles n`. -/ +noncomputable def truncGEXIsoOpcycles (n : ℤ) : + (K.truncGE n).X n ≅ K.opcycles n := + HomologicalComplex.truncGEXIsoOpcycles K (embeddingUpIntGE n) (i := 0) (by simp) + (by rw [boundaryGE_embeddingUpIntGE_iff]) + +/-- When `K` is a cochain complex indexed by `ℤ`, this is the isomorphism +`(K.truncLE n).X n ≅ K.cycles n`. -/ +noncomputable def truncLEXIsoCycles (n : ℤ) : + (K.truncLE n).X n ≅ K.cycles n := + HomologicalComplex.truncLEXIsoCycles K (embeddingUpIntLE n) (i := 0) (by simp) + (by rw [boundaryLE_embeddingUpIntLE_iff]) + +lemma acyclic_truncGE_iff (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁ := by lia) : + (K.truncGE n₁).Acyclic ↔ K.IsLE n₀ := by + dsimp [truncGE] + rw [acyclic_truncGE_iff_isSupportedOutside, + (Embedding.embeddingUpInt_areComplementary n₀ n₁ h).isSupportedOutside₂_iff] + +lemma acyclic_truncLE_iff (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁ := by lia) : + (K.truncLE n₀).Acyclic ↔ K.IsGE n₁ := by + dsimp [truncLE] + rw [acyclic_truncLE_iff_isSupportedOutside, + (Embedding.embeddingUpInt_areComplementary n₀ n₁ h).isSupportedOutside₁_iff] + +end HasZeroMorphisms section Abelian @@ -374,19 +449,33 @@ lemma shortComplexTruncLE_shortExact (n : ℤ) : (K.shortComplexTruncLE n).ShortExact := by apply HomologicalComplex.shortComplexTruncLE_shortExact -variable (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁) +variable (n₀ n₁ : ℤ) /-- The canonical morphism `(K.shortComplexTruncLE n₀).X₃ ⟶ K.truncGE n₁`. -/ -noncomputable abbrev shortComplexTruncLEX₃ToTruncGE : +noncomputable abbrev shortComplexTruncLEX₃ToTruncGE (h : n₀ + 1 = n₁ := by lia) : (K.shortComplexTruncLE n₀).X₃ ⟶ K.truncGE n₁ := HomologicalComplex.shortComplexTruncLEX₃ToTruncGE K (Embedding.embeddingUpInt_areComplementary n₀ n₁ h) @[reassoc] -lemma g_shortComplexTruncLEX₃ToTruncGE : +lemma g_shortComplexTruncLEX₃ToTruncGE (h : n₀ + 1 = n₁ := by lia) : (K.shortComplexTruncLE n₀).g ≫ K.shortComplexTruncLEX₃ToTruncGE n₀ n₁ h = K.πTruncGE n₁ := by apply HomologicalComplex.g_shortComplexTruncLEX₃ToTruncGE +lemma injective_opcycles [Injective (K.X n₀)] [Injective (K.X n₁)] + [K.IsStrictlyGE n₀] (hK : K.ExactAt n₀) (h : n₀ + 1 = n₁ := by lia) : + Injective (K.opcycles n₁) := by + let S : ShortComplex C := ShortComplex.mk (K.d n₀ n₁) (K.pOpcycles n₁) (by simp) + have : Mono S.f := by + let T := K.sc' (n₀ - 1) n₀ n₁ + have hT : T.Exact := by + rwa [← K.exactAt_iff' (n₀ - 1) n₀ n₁ (by simp) (by simpa)] + exact hT.mono_g ((K.isZero_of_isStrictlyGE n₀ _).eq_of_src ..) + have hS : S.ShortExact := + { exact := S.exact_of_g_is_cokernel (K.opcyclesIsCokernel n₀ n₁ (by simp [← h])) } + exact Retract.injective + { i := _, r := _, retract := (hS.splittingOfInjective).s_g } + end Abelian end CochainComplex diff --git a/Mathlib/Algebra/Homology/Embedding/IsSupported.lean b/Mathlib/Algebra/Homology/Embedding/IsSupported.lean index 9a1987a6598c4e..9f7fbefa73e179 100644 --- a/Mathlib/Algebra/Homology/Embedding/IsSupported.lean +++ b/Mathlib/Algebra/Homology/Embedding/IsSupported.lean @@ -161,6 +161,13 @@ instance map_isStrictlySupported [K.IsStrictlySupported e] : dsimp rw [← F.map_id, (K.isZero_X_of_isStrictlySupported e i' hi').eq_of_src (𝟙 _) 0, F.map_zero] +lemma isStrictlySupported_mapHomologicalComplex_obj_iff [F.Faithful] : + ((F.mapHomologicalComplex c').obj K).IsStrictlySupported e ↔ K.IsStrictlySupported e := by + refine ⟨fun _ ↦ ⟨fun i' hi' ↦ ?_⟩, fun _ ↦ inferInstance⟩ + rw [IsZero.iff_id_eq_zero] + exact F.map_injective ((isZero_X_of_isStrictlySupported + ((F.mapHomologicalComplex c').obj K) e i' hi').eq_of_src _ _) + end end HomologicalComplex diff --git a/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean b/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean index 4a3dfcb22871f7..bc11bec38b692a 100644 --- a/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean +++ b/Mathlib/Algebra/Homology/Embedding/TruncGEHomology.lean @@ -226,6 +226,11 @@ lemma acyclic_truncGE_iff_isSupportedOutside : variable {K L} +lemma Acyclic.truncGE (hK : K.Acyclic) (e : c.Embedding c') [e.IsTruncGE] : + (K.truncGE e).Acyclic := by + rw [acyclic_truncGE_iff_isSupportedOutside] + exact ⟨fun _ ↦ hK _⟩ + lemma quasiIso_truncGEMap_iff : QuasiIso (truncGEMap φ e) ↔ ∀ (i : ι) (i' : ι') (_ : e.f i = i'), QuasiIsoAt φ i' := by have : ∀ (i : ι) (i' : ι') (_ : e.f i = i'), diff --git a/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean b/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean index 4dce5e6f3311e7..d502131ca9842d 100644 --- a/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean +++ b/Mathlib/Algebra/Homology/Embedding/TruncLEHomology.lean @@ -84,6 +84,11 @@ lemma acyclic_truncLE_iff_isSupportedOutside : variable {K L} +lemma Acyclic.truncLE (hK : K.Acyclic) (e : c.Embedding c') [e.IsTruncLE] : + (K.truncLE e).Acyclic := by + rw [acyclic_truncLE_iff_isSupportedOutside] + exact ⟨fun _ ↦ hK _⟩ + lemma quasiIso_truncLEMap_iff : QuasiIso (truncLEMap φ e) ↔ ∀ (i : ι) (i' : ι') (_ : e.f i = i'), QuasiIsoAt φ i' := by rw [← quasiIso_opFunctor_map_iff] diff --git a/Mathlib/Algebra/Homology/Factorizations/CM5a.lean b/Mathlib/Algebra/Homology/Factorizations/CM5a.lean index 30242a2ebf1749..a8b90f9b7f277b 100644 --- a/Mathlib/Algebra/Homology/Factorizations/CM5a.lean +++ b/Mathlib/Algebra/Homology/Factorizations/CM5a.lean @@ -5,10 +5,9 @@ Authors: Joël Riou -/ module -public import Mathlib.Algebra.Homology.DerivedCategory.HomologySequence +public import Mathlib.Algebra.Homology.DerivedCategory.TStructure public import Mathlib.Algebra.Homology.Factorizations.CM5b public import Mathlib.Algebra.Homology.HomologicalComplexLimitsEventuallyConstant -public import Mathlib.Algebra.Homology.Refinements public import Mathlib.Algebra.Homology.SingleHomology public import Mathlib.CategoryTheory.Category.Factorisation public import Mathlib.CategoryTheory.Functor.OfSequence @@ -49,6 +48,7 @@ lemma `cm5a_cof`. -/ + open CategoryTheory Limits Opposite Abelian HomologicalComplex Pretriangulated variable {C : Type*} [Category* C] [Abelian C] @@ -218,8 +218,7 @@ a factorisation of `f` as `ι f n ≫ π f n = f` where `ι f n : K ⟶ mid f n` is a monomorphism which is a quasi-isomorphism in degrees `≤ n`, `π f n` is a degreewise epimorphism with an injective kernel which also induces isomorphisms in degrees `≤ n`. - -/ - +-/ open HomComplex @@ -652,4 +651,43 @@ public lemma cm5a (n : ℤ) [K.IsStrictlyGE (n + 1)] [L.IsStrictlyGE n] : exact ⟨K', inferInstance, ι, π ≫ p, inferInstance, inferInstance, MorphismProperty.comp_mem _ _ _ hπ hp, by simp⟩ +open ZeroObject + +variable (K) + +public lemma exists_mono_quasiIso_injective (n₀ n₁ : ℤ) (h : n₀ + 1 = n₁ := by lia) + [K.IsStrictlyGE n₁] : + ∃ (L : CochainComplex C ℤ) (i : K ⟶ L) (_hi : Mono i) (_hi' : QuasiIso i) + (_ : ∀ (n : ℤ), Injective (L.X n)), L.IsStrictlyGE n₀ := by + have : K.IsStrictlyGE (n₀ + 1) := by rw [h]; infer_instance + obtain ⟨L, hL, i, p, hi, hi', hp, _⟩ := cm5a (0 : K ⟶ 0) n₀ + exact ⟨L, i, hi, hi', (degreewiseEpiWithInjectiveKernel_iff_of_isZero p + (Limits.isZero_zero _)).1 hp, hL⟩ + +public lemma exists_quasiIso_injective (n : ℤ) [K.IsStrictlyGE n] : + ∃ (L : CochainComplex C ℤ) (i : K ⟶ L) (_hi' : QuasiIso i) + (_hL : ∀ (n : ℤ), Injective (L.X n)), L.IsStrictlyGE n := by + /- The proof proceeds by first applying `exists_mono_quasiIso_injective` in order to + obtain a monomorphism `K ⟶ L` that is also a quasi-isomorphism + with `L` consisting of injective objects and `L` lying in degrees `≥ n - 1`. + Then, as it is quasi-isomorphic to `K`, the cochain complex `L` is cohomologically + in degrees `≥ n`, so that the composition `K ⟶ L ⟶ L.truncGE n` is a quasi-isomorphism. + In order to conclude, one needs to show that `(L.truncGE n).X n` is injective, + i.e. that `L.opcycles n` is injective. -/ + have : HasDerivedCategory C := MorphismProperty.HasLocalization.standard _ + obtain ⟨L, i, _, _, hL, _⟩ := exists_mono_quasiIso_injective K (n - 1) n (by simp) + have : L.IsGE n := by + have hK : K.IsGE n := inferInstance + rw [← DerivedCategory.isGE_Q_obj_iff] at hK ⊢ + exact DerivedCategory.TStructure.t.isGE_of_iso (asIso (DerivedCategory.Q.map i)) n + have : QuasiIso (L.πTruncGE n) := (L.quasiIso_πTruncGE_iff n).mpr inferInstance + have : Injective (L.opcycles n) := + L.injective_opcycles (n - 1) n (L.exactAt_of_isGE n (n - 1)) + -- note: this `i ≫ L.πTruncGE n` is a mono in degrees > n, but it may not be in degree n + refine ⟨L.truncGE n, i ≫ L.πTruncGE n, inferInstance, fun q ↦ ?_, inferInstance⟩ + obtain h | rfl | h := lt_trichotomy q n + · exact (isZero_of_isStrictlyGE _ n _ h).injective + · exact Injective.of_iso (L.truncGEXIsoOpcycles q).symm inferInstance + · exact Injective.of_iso (L.truncGEXIso n q h).symm (hL q) + end CochainComplex.Plus.modelCategoryQuillen diff --git a/Mathlib/Algebra/Homology/FullSubcategory.lean b/Mathlib/Algebra/Homology/FullSubcategory.lean new file mode 100644 index 00000000000000..ad1d853e76f99d --- /dev/null +++ b/Mathlib/Algebra/Homology/FullSubcategory.lean @@ -0,0 +1,45 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.Algebra.Homology.HomologicalComplex + +/-! +# Homological complexes in full subcategories + +-/ + +@[expose] public section + +open CategoryTheory + +namespace HomologicalComplex + +/-- Given `P : ObjectProperty V` and `K : HomologicalComplex V c`, +this is a lift of `K` in `HomologicalComplex P.FullSubcategory c` +when `K.X n` satisfies `P` for all `n`. -/ +@[simps X d] +def liftObjectProperty {ι : Type*} {c : ComplexShape ι} + {V : Type*} [Category* V] [Preadditive V] (P : ObjectProperty V) + (K : HomologicalComplex V c) (hK : ∀ (n : ι), P (K.X n)) : + HomologicalComplex P.FullSubcategory c where + X n := ⟨_, hK n⟩ + d i j := ObjectProperty.homMk (K.d i j) + +set_option backward.defeqAttrib.useBackward true in +/-- The functor `D ⥤ HomologicalComplex P.FullSubcategory c` +which is obtained by lifting a functor `D ⥤ HomologicalComplex V c` +when for any `X : D` and `n`, the object `(F.obj X).X n` +satisfies a property `P : ObjectProperty V`. -/ +@[simps] +def liftFunctorObjectProperty {D : Type*} [Category* D] {ι : Type*} {c : ComplexShape ι} + {V : Type*} [Category* V] [Preadditive V] (P : ObjectProperty V) + (F : D ⥤ HomologicalComplex V c) (hF : ∀ (X : D) (n : ι), P ((F.obj X).X n)) : + D ⥤ HomologicalComplex P.FullSubcategory c where + obj X := liftObjectProperty _ (F.obj X) (hF X) + map f := { f n := ObjectProperty.homMk ((F.map f).f n) } + +end HomologicalComplex diff --git a/Mathlib/Algebra/Homology/HomologicalComplex.lean b/Mathlib/Algebra/Homology/HomologicalComplex.lean index 99e9a323ad7da9..c98ed1fc1a863a 100644 --- a/Mathlib/Algebra/Homology/HomologicalComplex.lean +++ b/Mathlib/Algebra/Homology/HomologicalComplex.lean @@ -616,8 +616,7 @@ instance (f : C₁ ⟶ C₂) [IsSplitMono f] (j : ι) : IsSplitMono (f.f j) := inferInstanceAs (IsSplitMono ((eval _ _ j).map f)) @[push ←, simp] -lemma inv_f_apply (f : C₁ ⟶ C₂) [IsIso f] (j : ι) : - (inv f).f j = inv (f.f j) := by +lemma inv_f_apply (f : C₁ ⟶ C₂) [IsIso f] (j : ι) : (inv f).f j = inv (f.f j) := by apply IsIso.eq_inv_of_inv_hom_id simp [← comp_f] diff --git a/Mathlib/Algebra/Homology/HomologicalComplexLimitsEventuallyConstant.lean b/Mathlib/Algebra/Homology/HomologicalComplexLimitsEventuallyConstant.lean index c76e40fddbe9b2..c35a7343e85181 100644 --- a/Mathlib/Algebra/Homology/HomologicalComplexLimitsEventuallyConstant.lean +++ b/Mathlib/Algebra/Homology/HomologicalComplexLimitsEventuallyConstant.lean @@ -25,7 +25,7 @@ public section open CategoryTheory Category Limits variable {C J ι : Type*} [Category C] [Category J] - {c : ComplexShape ι} [IsCofiltered J] + {c : ComplexShape ι} [IsCofiltered J] namespace HomologicalComplex diff --git a/Mathlib/Algebra/Homology/HomotopyCategory.lean b/Mathlib/Algebra/Homology/HomotopyCategory.lean index 099d7b695a351b..398b611bbac818 100644 --- a/Mathlib/Algebra/Homology/HomotopyCategory.lean +++ b/Mathlib/Algebra/Homology/HomotopyCategory.lean @@ -285,4 +285,64 @@ instance (F : V ⥤ W) [F.Additive] (c : ComplexShape ι) [Linear R V] [Linear R have := Functor.linear_of_iso R (F.mapHomotopyCategoryFactors c).symm (HomotopyCategory.quotient V c).linear_of_full_essSurj_comp (F.mapHomotopyCategory c) +/-- If additive functors are related by an isomorphism `F ⋙ G ≅ H`, this is +the corresponding isomorphism for the induced functors on homotopy categories +of homological complexes. -/ +def Functor.mapHomotopyCategoryCompIso {W' : Type*} [Category W'] [Preadditive W'] + {F : V ⥤ W} {G : W ⥤ W'} {H : V ⥤ W'} (e : F ⋙ G ≅ H) + [F.Additive] [G.Additive] [H.Additive] (c : ComplexShape ι) : + F.mapHomotopyCategory c ⋙ G.mapHomotopyCategory c ≅ H.mapHomotopyCategory c := + Quotient.natIsoLift _ (isoWhiskerRight (Functor.mapHomologicalComplexCompIso e c) + (HomotopyCategory.quotient W' c)) + +variable {c} in +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +/-- The preimage by a fully faithful functor of a homotopy between morphisms +of homological complexes. -/ +def Functor.preimageHomotopy + (F : V ⥤ W) [F.Additive] [F.Full] [F.Faithful] + {K L : HomologicalComplex V c} {f₁ f₂ : K ⟶ L} + (H : Homotopy ((F.mapHomologicalComplex c).map f₁) ((F.mapHomologicalComplex c).map f₂)) : + Homotopy f₁ f₂ where + hom i j := F.preimage (H.hom i j) + zero i j hij := F.map_injective (by simp only [map_preimage, Functor.map_zero, H.zero i j hij]) + comm i := F.map_injective (by simp [dsimp% H.comm i, dNext, prevD]) + +instance (F : V ⥤ W) [F.Full] [F.Faithful] [F.Additive] : + (F.mapHomotopyCategory c).Faithful where + map_injective := by + rintro ⟨K⟩ ⟨L⟩ f₁ f₂ h + obtain ⟨f₁, rfl⟩ := (HomotopyCategory.quotient _ _).map_surjective f₁ + obtain ⟨f₂, rfl⟩ := (HomotopyCategory.quotient _ _).map_surjective f₂ + exact HomotopyCategory.eq_of_homotopy _ _ + (F.preimageHomotopy (HomotopyCategory.homotopyOfEq _ _ h)) + +instance (F : V ⥤ W) [F.Full] [F.Faithful] [F.Additive] : + (F.mapHomotopyCategory c).Full where + map_surjective := by + rintro ⟨K⟩ ⟨L⟩ ⟨f⟩ + obtain ⟨g : K ⟶ L, rfl⟩ := (F.mapHomologicalComplex c).map_surjective f + exact ⟨(HomotopyCategory.quotient V c).map g, rfl⟩ + end CategoryTheory + +namespace HomologicalComplex + +variable {ι : Type*} {V : Type u} [Category.{v} V] [Preadditive V] {c : ComplexShape ι} + +open HomotopyCategory in +lemma isIso_quotient_map_iff_homotopyEquivalences + {K L : HomologicalComplex V c} (f : K ⟶ L) : + IsIso ((quotient _ _).map f) ↔ + homotopyEquivalences _ _ f := by + refine ⟨fun _ ↦ ?_, fun hf ↦ quotient_inverts_homotopyEquivalences V c f hf⟩ + obtain ⟨g, hg⟩ := (quotient V c).map_surjective (inv ((quotient V c).map f)) + let e : HomotopyEquiv K L := + { hom := f + inv := g + homotopyHomInvId := HomotopyCategory.homotopyOfEq _ _ (by cat_disch) + homotopyInvHomId := HomotopyCategory.homotopyOfEq _ _ (by cat_disch) } + exact ⟨e, rfl⟩ + +end HomologicalComplex diff --git a/Mathlib/Algebra/Homology/HomotopyCategory/HomComplexInduction.lean b/Mathlib/Algebra/Homology/HomotopyCategory/HomComplexInduction.lean index e852f802f684b3..b1c8cb3f37c129 100644 --- a/Mathlib/Algebra/Homology/HomotopyCategory/HomComplexInduction.lean +++ b/Mathlib/Algebra/Homology/HomotopyCategory/HomComplexInduction.lean @@ -40,7 +40,7 @@ def EqUpTo {n : ℤ} (α β : Cochain K L n) (p₀ : ℤ) : Prop := namespace InductionUp variable {d : ℤ} {X : ℕ → Set (Cochain K L d)} (φ : ∀ (n : ℕ), X n → X (n + 1)) - {p₀ : ℤ} (hφ : ∀ (n : ℕ) (x : X n), (φ n x).val.EqUpTo x.val (p₀ + n)) (x₀ : X 0) + {p₀ : ℤ} (hφ : ∀ (n : ℕ) (x : X n), (φ n x).val.EqUpTo x.val (p₀ + n)) (x₀ : X 0) /-- Assuming we have a sequence of subsets `X n : Set (Cochain K L d)` for all `n : ℕ`, a sequence of maps `φ n : X n → X (n + 1)` for `n : ℕ`, and an element `x₀ : X 0`, diff --git a/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean b/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean index 505b25c297c77f..352d3a8ee1bca5 100644 --- a/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean +++ b/Mathlib/Algebra/Homology/HomotopyCategory/Plus.lean @@ -23,7 +23,7 @@ of `HomotopyCategory C (.up ℤ)` consisting of bounded below cochain complexes. open CategoryTheory Limits ZeroObject Pretriangulated HomotopicalAlgebra -variable (C : Type*) [Category* C] [Preadditive C] +variable (C D : Type*) [Category* C] [Category* D] [Preadditive C] [Preadditive D] (A : Type*) [Category* A] [Abelian A] namespace CochainComplex @@ -248,3 +248,61 @@ end end Plus end HomotopyCategory + +namespace CategoryTheory + +namespace Functor + +variable {C D} +variable (F : C ⥤ D) [F.Additive] + +set_option backward.defeqAttrib.useBackward true in +/-- The functor between bounded below homotopy categories that is induced +by an additive functor. -/ +def mapHomotopyCategoryPlus : HomotopyCategory.Plus C ⥤ HomotopyCategory.Plus D := + (HomotopyCategory.plus D).lift + (HomotopyCategory.Plus.ι C ⋙ F.mapHomotopyCategory (ComplexShape.up ℤ)) (by + rintro ⟨X, hX⟩ + obtain ⟨K, rfl⟩ := HomotopyCategory.quotient_obj_surjective X + dsimp + simp only [HomotopyCategory.plus_quotient_obj_iff] at hX ⊢ + obtain ⟨n, _⟩ := hX + exact ⟨n, inferInstanceAs (CochainComplex.IsStrictlyGE + ((F.mapHomologicalComplex _).obj K) n)⟩) + +noncomputable instance : + F.mapHomotopyCategoryPlus.CommShift ℤ := + inferInstanceAs (((HomotopyCategory.plus D).lift (HomotopyCategory.Plus.ι C ⋙ + F.mapHomotopyCategory (.up ℤ)) _).CommShift ℤ) + +set_option backward.isDefEq.respectTransparency false in +instance [HasZeroObject C] [HasBinaryBiproducts C] [HasZeroObject D] [HasBinaryBiproducts D] : + (F.mapHomotopyCategoryPlus).IsTriangulated := by + dsimp only [mapHomotopyCategoryPlus] + infer_instance + +instance [Full F] [Faithful F] : Full F.mapHomotopyCategoryPlus where + map_surjective f := + ⟨ObjectProperty.homMk ((F.mapHomotopyCategory _).preimage f.hom), by + ext + exact (F.mapHomotopyCategory _).map_preimage f.hom⟩ + +instance [Full F] [Faithful F] : Faithful F.mapHomotopyCategoryPlus where + map_injective h := by + ext + exact (F.mapHomotopyCategory _).map_injective ((ObjectProperty.ι _).congr_map h) + +/-- Given additive functors that are related by an isomorphism `F ⋙ G ≅ H`, this is +the corresponding isomorphism on the corresponding functor between +the bounded below homotopy categories. -/ +def mapHomotopyCategoryPlusCompIso {E : Type*} [Category* E] [Preadditive E] + {F : C ⥤ D} {G : D ⥤ E} {H : C ⥤ E} (e : F ⋙ G ≅ H) + [F.Additive] [G.Additive] [H.Additive] : + F.mapHomotopyCategoryPlus ⋙ G.mapHomotopyCategoryPlus ≅ H.mapHomotopyCategoryPlus := + ((HomotopyCategory.plus _).fullyFaithfulι.whiskeringRight _).preimageIso + (isoWhiskerLeft (HomotopyCategory.Plus.ι C) + (mapHomotopyCategoryCompIso e (.up ℤ))) + +end Functor + +end CategoryTheory diff --git a/Mathlib/Algebra/Homology/HomotopyCofiber.lean b/Mathlib/Algebra/Homology/HomotopyCofiber.lean index aceb7b4f4d7f86..96920cb78c75bc 100644 --- a/Mathlib/Algebra/Homology/HomotopyCofiber.lean +++ b/Mathlib/Algebra/Homology/HomotopyCofiber.lean @@ -145,6 +145,33 @@ lemma inrX_fstX (i j : ι) (hij : c.Rel i j) : obtain rfl := c.next_eq' hij simp [inrX, fstX, dif_pos hij] +@[reassoc (attr := simp)] +lemma inlX_XIsoBiprod_hom (i j : ι) (hij : c.Rel j i) : + haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij + inlX φ i j hij ≫ (XIsoBiprod φ j i hij).hom = biprod.inl := by + haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij + simp [inlX] + +@[reassoc (attr := simp)] +lemma inl_XIsoBiprod_inv (i j : ι) (hij : c.Rel j i) : + haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij + biprod.inl ≫ (XIsoBiprod φ j i hij).inv = inlX φ i j hij := by + simp [inlX] + +@[reassoc (attr := simp)] +lemma inrX_XIsoBiprod_hom (i j : ι) (hij : c.Rel j i) : + haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij + inrX φ j ≫ (XIsoBiprod φ j i hij).hom = biprod.inr := by + obtain rfl := c.next_eq' hij + haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij + simp [inrX, XIsoBiprod, dif_pos hij] + +@[reassoc (attr := simp)] +lemma inr_XIsoBiprod_inv (i j : ι) (hij : c.Rel j i) : + haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij + biprod.inr ≫ (XIsoBiprod φ j i hij).inv = inrX φ j := by + rw [← inrX_XIsoBiprod_hom φ i j hij, Category.assoc, Iso.hom_inv_id, Category.comp_id] + /-- The `d` field of the homological complex `homotopyCofiber φ`. -/ noncomputable def d (i j : ι) : X φ i ⟶ X φ j := if hij : c.Rel i j @@ -175,9 +202,9 @@ lemma ext_from_X (i j : ι) (hij : c.Rel j i) {A : C} {f g : X φ j ⟶ A} haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hij rw [← cancel_epi (XIsoBiprod φ j i hij).inv] apply biprod.hom_ext' - · simpa [inlX] using h₁ + · simpa · obtain rfl := c.next_eq' hij - simpa [inrX, dif_pos hij] using h₂ + simpa [-inr_XIsoBiprod_inv, -inr_XIsoBiprod_inv_assoc, inrX, dif_pos hij] using h₂ lemma ext_from_X' (i : ι) (hi : ¬ c.Rel i (c.next i)) {A : C} {f g : X φ i ⟶ A} (h : inrX φ i ≫ f = inrX φ i ≫ g) : f = g := by @@ -384,6 +411,131 @@ noncomputable def descEquiv (K : HomologicalComplex C c) (hc : ∀ j, ∃ i, c.R rw [descSigma_ext_iff] cat_disch +section + +variable {F' F'' G' G'' : HomologicalComplex C c} (φ' : F' ⟶ G') (φ'' : F'' ⟶ G'') + [HasHomotopyCofiber φ'] [HasHomotopyCofiber φ''] + (H : ∀ (j : ι), ∃ i, c.Rel i j) + +set_option backward.defeqAttrib.useBackward true in +/-- The morphism between homotopy cofibers that is induced by a +morphism of arrows. -/ +noncomputable def mapArrowHom (α : Arrow.mk φ ⟶ Arrow.mk φ') : + homotopyCofiber φ ⟶ homotopyCofiber φ' := + desc _ (α.right ≫ homotopyCofiber.inr φ') + ((Homotopy.ofEq (by + simp [reassoc_of% dsimp% α.w])).trans (((inrCompHomotopy φ' H).compLeft α.left).trans + (Homotopy.ofEq (by simp)))) + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +@[simp] +lemma mapArrowHom_id : mapArrowHom φ φ H (𝟙 _) = 𝟙 _ := by + ext i + dsimp + by_cases hi : c.Rel i (c.next i) + · refine ext_to_X _ _ _ hi ?_ ?_ + all_goals simp [mapArrowHom, desc_f _ _ _ _ _ hi, inrCompHomotopy_hom _ _ _ _ hi] + · exact ext_to_X' _ _ hi (by simp [mapArrowHom, desc_f' _ _ _ _ hi]) + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +@[reassoc] +lemma mapArrowHom_comp + (α : Arrow.mk φ ⟶ Arrow.mk φ') (β : Arrow.mk φ' ⟶ Arrow.mk φ'') : + mapArrowHom φ φ'' H (α ≫ β) = mapArrowHom φ φ' H α ≫ mapArrowHom φ' φ'' H β := by + ext i + dsimp + by_cases hi : c.Rel i (c.next i) + · refine ext_to_X _ _ _ hi ?_ ?_ + all_goals simp [mapArrowHom, desc_f _ _ _ _ _ hi, inrCompHomotopy_hom _ _ _ _ hi] + · exact ext_to_X' _ _ hi (by simp [mapArrowHom, desc_f' _ _ _ _ hi]) + +/-- The isomorphism between homotopy cofibers that is induced by an +isomorphism of arrows. -/ +@[simps] +noncomputable def mapArrowIso (α : Arrow.mk φ ≅ Arrow.mk φ') : + homotopyCofiber φ ≅ homotopyCofiber φ' where + hom := mapArrowHom φ φ' H α.hom + inv := mapArrowHom φ' φ H α.inv + hom_inv_id := by rw [← mapArrowHom_comp, Iso.hom_inv_id, mapArrowHom_id] + inv_hom_id := by rw [← mapArrowHom_comp, Iso.inv_hom_id, mapArrowHom_id] + +end + +section + +variable {D : Type*} [Category* D] [Preadditive D] (H : C ⥤ D) [H.Additive] + [HasHomotopyCofiber ((H.mapHomologicalComplex c).map φ)] + +/-- Auxiliary definition for `mapHomologicalComplexObjIso`. -/ +noncomputable def mapHomologicalComplexObjXIso (i : ι) : + H.obj ((homotopyCofiber φ).X i) ≅ + (homotopyCofiber ((H.mapHomologicalComplex c).map φ)).X i := + if hi : c.Rel i (c.next i) + then by + haveI := preservesBinaryBiproducts_of_preservesBiproducts H + haveI := HasHomotopyCofiber.hasBinaryBiproduct φ _ _ hi + haveI := HasHomotopyCofiber.hasBinaryBiproduct ((H.mapHomologicalComplex c).map φ) _ _ hi + exact H.mapIso (homotopyCofiber.XIsoBiprod φ _ _ hi) ≪≫ H.mapBiprod _ _ ≪≫ + (homotopyCofiber.XIsoBiprod ((H.mapHomologicalComplex c).map φ) _ _ hi).symm + else H.mapIso (homotopyCofiber.XIso φ i hi) ≪≫ + (homotopyCofiber.XIso ((H.mapHomologicalComplex c).map φ) i hi).symm + +set_option backward.isDefEq.respectTransparency false in +@[reassoc (attr := simp)] +lemma inlX_mapHomologicalComplexObjXIso_inv + (i j : ι) (hij : c.Rel j i) : + inlX ((H.mapHomologicalComplex c).map φ) i j hij ≫ + (mapHomologicalComplexObjXIso φ H j).inv = H.map (inlX φ i j hij) := by + obtain rfl := c.next_eq' hij + simp [mapHomologicalComplexObjXIso, dif_pos hij, ← Functor.map_comp] + +set_option backward.isDefEq.respectTransparency false in +@[reassoc (attr := simp)] +lemma inrX_mapHomologicalComplexObjXIso_inv (i : ι) : + inrX ((H.mapHomologicalComplex c).map φ) i ≫ + (mapHomologicalComplexObjXIso φ H i).inv = H.map (inrX φ i) := by + by_cases hi : c.Rel i (c.next i) + · simp [mapHomologicalComplexObjXIso, dif_pos hi, ← Functor.map_comp] + · dsimp [mapHomologicalComplexObjXIso, XIso, inrX] + simp [dif_neg hi] + +set_option backward.isDefEq.respectTransparency false in +@[reassoc (attr := simp)] +lemma map_inrX_mapHomologicalComplexObjXIso_hom (i : ι) : + H.map (inrX φ i) ≫ (mapHomologicalComplexObjXIso φ H i).hom = + inrX ((H.mapHomologicalComplex c).map φ) i := by + rw [← inrX_mapHomologicalComplexObjXIso_inv_assoc, Iso.inv_hom_id, comp_id] + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +/-- The isomorphism expressing the commutation between taking +the homotopy cofiber of a morphism of homological complexes and +applying an additive functor. -/ +noncomputable def mapHomologicalComplexObjIso : + (H.mapHomologicalComplex c).obj (homotopyCofiber φ) ≅ + homotopyCofiber ((H.mapHomologicalComplex c).map φ) := + Iso.symm (HomologicalComplex.Hom.isoOfComponents + (fun i ↦ (mapHomologicalComplexObjXIso φ H i).symm) + (fun i j hij ↦ by + dsimp + apply ext_from_X _ _ _ hij + · by_cases hj : c.Rel j (c.next j) + · simp [← Functor.map_comp, inlX_d _ _ _ _ _ hj, inlX_d_assoc _ _ _ _ _ hj] + · simp [← Functor.map_comp, inlX_d' _ _ _ _ hj, inlX_d'_assoc _ _ _ _ hj] + · simp [← Functor.map_comp])) + +set_option backward.isDefEq.respectTransparency false in +@[reassoc (attr := simp)] +lemma inr_mapHomologicalComplexObjIso_hom : + (H.mapHomologicalComplex c).map (inr φ) ≫ + (mapHomologicalComplexObjIso φ H).hom = inr _ := by + ext + simp [mapHomologicalComplexObjIso] + +end + end homotopyCofiber section @@ -565,6 +717,56 @@ lemma map_ι₀_eq_map_ι₁ {D : Type*} [Category* D] (H : HomologicalComplex C end + +section + +variable (F) {D : Type*} [Category* D] [Preadditive D] (H : C ⥤ D) [H.Additive] + [∀ i, HasBinaryBiproduct (F.X i) (F.X i)] + [HasHomotopyCofiber (biprod.lift (𝟙 F) (-𝟙 F))] + [∀ i, HasBinaryBiproduct (((H.mapHomologicalComplex c).obj F).X i) + (((H.mapHomologicalComplex c).obj F).X i)] + [HasHomotopyCofiber (biprod.lift (𝟙 ((H.mapHomologicalComplex c).obj F)) + (-𝟙 ((H.mapHomologicalComplex c).obj F)))] + [HasHomotopyCofiber ((H.mapHomologicalComplex c).map (biprod.lift (𝟙 F) (-𝟙 F)))] + (hc : ∀ (j : ι), ∃ i, c.Rel i j) + +attribute [local instance] preservesBinaryBiproduct_of_preservesBiproduct + +set_option backward.defeqAttrib.useBackward true in +/-- The isomorphism expressing the commutation between taking +the cylinder of a homological complex and applying an additive functor. -/ +noncomputable def mapHomologicalComplexObjIso : + (H.mapHomologicalComplex c).obj (cylinder F) ≅ + cylinder ((H.mapHomologicalComplex c).obj F) := + homotopyCofiber.mapHomologicalComplexObjIso _ H ≪≫ + homotopyCofiber.mapArrowIso _ _ hc + (Arrow.isoMk (Iso.refl _) ((H.mapHomologicalComplex c).mapBiprod F F) (by + apply biprod.hom_ext <;> simp [← Functor.map_comp])) + +set_option backward.defeqAttrib.useBackward true in +@[reassoc (attr := simp)] +lemma map_ι₀_mapHomologicalComplexObjIso_hom : + (H.mapHomologicalComplex c).map (cylinder.ι₀ F) ≫ (mapHomologicalComplexObjIso F H hc).hom = + cylinder.ι₀ _ := by + dsimp [mapHomologicalComplexObjIso, ι₀, homotopyCofiber.mapArrowHom] + rw [Functor.map_comp, assoc, homotopyCofiber.inr_mapHomologicalComplexObjIso_hom_assoc, + homotopyCofiber.inr_desc, ← Category.assoc] + congr 1 + apply biprod.hom_ext <;> simp [← Functor.map_comp] + +set_option backward.defeqAttrib.useBackward true in +@[reassoc (attr := simp)] +lemma map_ι₁_mapHomologicalComplexObjIso_hom : + (H.mapHomologicalComplex c).map (cylinder.ι₁ F) ≫ (mapHomologicalComplexObjIso F H hc).hom = + cylinder.ι₁ _ := by + dsimp [mapHomologicalComplexObjIso, ι₁, homotopyCofiber.mapArrowHom] + rw [Functor.map_comp, assoc, homotopyCofiber.inr_mapHomologicalComplexObjIso_hom_assoc, + homotopyCofiber.inr_desc, ← Category.assoc] + congr 1 + apply biprod.hom_ext <;> simp [← Functor.map_comp] + +end + end cylinder omit [DecidableRel c.Rel] in diff --git a/Mathlib/Algebra/Homology/HomotopyFiber.lean b/Mathlib/Algebra/Homology/HomotopyFiber.lean index 1705df5e21c76b..24d5c09a3cec5c 100644 --- a/Mathlib/Algebra/Homology/HomotopyFiber.lean +++ b/Mathlib/Algebra/Homology/HomotopyFiber.lean @@ -161,6 +161,48 @@ lemma lift_π₁ : lift φ₀ φ₁ h ≫ π₁ K = φ₁ := end +section + +variable (F) {D : Type*} [Category* D] [Preadditive D] (H : C ⥤ D) [H.Additive] + [∀ (i : α), HasBinaryBiproduct (((H.mapHomologicalComplex c).obj K).X i) + (((H.mapHomologicalComplex c).obj K).X i)] + [((H.mapHomologicalComplex c).obj K).HasPathObject] + +variable + [∀ (i : α), + HasBinaryBiproduct (((H.op.mapHomologicalComplex c.symm).obj K.op).X i) + (((H.op.mapHomologicalComplex c.symm).obj K.op).X i)] + [HasHomotopyCofiber (biprod.lift (𝟙 ((H.op.mapHomologicalComplex c.symm).obj K.op)) + (-𝟙 ((H.op.mapHomologicalComplex c.symm).obj K.op)))] + [HasHomotopyCofiber ((H.op.mapHomologicalComplex c.symm).map (biprod.lift (𝟙 K.op) (-𝟙 K.op)))] + [∀ (i : α), HasBinaryBiproduct (K.op.X i) (K.op.X i)] + +variable (hc : ∀ (i : α), ∃ j, c.Rel i j) + +/-- The isomorphism expressing the commutation between taking +the path object of a homological complex and applying an additive functor. -/ +@[no_expose] +noncomputable def mapHomologicalComplexObjIso : + (H.mapHomologicalComplex c).obj (K.pathObject) ≅ + pathObject ((H.mapHomologicalComplex c).obj K) := + (unopFunctor _ _).mapIso (cylinder.mapHomologicalComplexObjIso K.op H.op hc).op.symm + +@[reassoc (attr := simp)] +lemma mapHomologicalComplexObjIso_inv_map_π₀ : + (mapHomologicalComplexObjIso K H hc).inv ≫ (H.mapHomologicalComplex c).map (π₀ K) = + π₀ _ := + Quiver.Hom.op_inj ((opFunctor _ _).map_injective + (cylinder.map_ι₀_mapHomologicalComplexObjIso_hom K.op H.op hc)) + +@[reassoc (attr := simp)] +lemma mapHomologicalComplexObjIso_inv_map_π₁ : + (mapHomologicalComplexObjIso K H hc).inv ≫ (H.mapHomologicalComplex c).map (π₁ K) = + π₁ _ := + Quiver.Hom.op_inj ((opFunctor _ _).map_injective + (cylinder.map_ι₁_mapHomologicalComplexObjIso_hom K.op H.op hc)) + +end + end pathObject end HomologicalComplex diff --git a/Mathlib/Algebra/Homology/Opposite.lean b/Mathlib/Algebra/Homology/Opposite.lean index ede453bfc5cf30..f839402d2fa9bc 100644 --- a/Mathlib/Algebra/Homology/Opposite.lean +++ b/Mathlib/Algebra/Homology/Opposite.lean @@ -13,6 +13,7 @@ public import Mathlib.Algebra.Homology.QuasiIso /-! # Opposite categories of complexes + Given a preadditive category `V`, the opposite of its category of chain complexes is equivalent to the category of cochain complexes of objects in `Vᵒᵖ`. We define this equivalence, and another analogous equivalence (for a general category of homological complexes with a general diff --git a/Mathlib/Algebra/Homology/Precylinder.lean b/Mathlib/Algebra/Homology/Precylinder.lean index d232a46034ba91..24b5e19930be59 100644 --- a/Mathlib/Algebra/Homology/Precylinder.lean +++ b/Mathlib/Algebra/Homology/Precylinder.lean @@ -38,7 +38,7 @@ noncomputable def precylinder [K.HasCylinder] : Precylinder K where π := cylinder.π _ /-- The pre-path object of a homological complex that is given by - `HomologicalComplex.pathObject`. -/ +`HomologicalComplex.pathObject`. -/ @[simps] noncomputable def prepathObject [K.HasPathObject] : PrepathObject K where P := K.pathObject diff --git a/Mathlib/Algebra/Homology/ShortComplex/Exact.lean b/Mathlib/Algebra/Homology/ShortComplex/Exact.lean index f02dbe63a3ce37..fee0ba6b919258 100644 --- a/Mathlib/Algebra/Homology/ShortComplex/Exact.lean +++ b/Mathlib/Algebra/Homology/ShortComplex/Exact.lean @@ -261,6 +261,12 @@ lemma Exact.isZero_of_both_zeros (ex : S.Exact) (hf : S.f = 0) (hg : S.g = 0) : IsZero S.X₂ := (ShortComplex.HomologyData.ofZeros S hf hg).exact_iff.1 ex +/-- In an exact short complex, if the two outer objects are zero objects, then so is the +middle object. -/ +lemma Exact.isZero_of_both_isZero (ex : S.Exact) (hX₁ : IsZero S.X₁) (hX₃ : IsZero S.X₃) : + IsZero S.X₂ := + ex.isZero_of_both_zeros (hX₁.eq_zero_of_src _) (hX₃.eq_zero_of_tgt _) + end section Preadditive diff --git a/Mathlib/Algebra/Homology/SpectralObject/Page.lean b/Mathlib/Algebra/Homology/SpectralObject/Page.lean index 4b7662ad43bb1e..e238bd05a5dfd6 100644 --- a/Mathlib/Algebra/Homology/SpectralObject/Page.lean +++ b/Mathlib/Algebra/Homology/SpectralObject/Page.lean @@ -236,11 +236,8 @@ noncomputable def πE (hn₁ : n₀ + 1 = n₁ := by lia) (hn₂ : n₁ + 1 = n (X.cyclesIso f₁ f₂ f₃ n₀ n₁ n₂).inv ≫ (X.shortComplex f₁ f₂ f₃ n₀ n₁ n₂).homologyπ -#adaptation_note /-- nightly-2026-03-04 -The `deriving` keyword on a `def` should just apply `noncomputable` to all -instances automatically if the main `def` is already `noncomputable`. -/ set_option backward.isDefEq.respectTransparency false in -deriving noncomputable instance Epi for πE +deriving instance Epi for πE set_option backward.isDefEq.respectTransparency false in @[reassoc (attr := simp)] diff --git a/Mathlib/Algebra/Lie/CartanMatrix.lean b/Mathlib/Algebra/Lie/CartanMatrix.lean deleted file mode 100644 index f9cb2e5bcd63b5..00000000000000 --- a/Mathlib/Algebra/Lie/CartanMatrix.lean +++ /dev/null @@ -1,7 +0,0 @@ -module -- shake: keep-all - -import Mathlib.Data.Finset.Attr -import Mathlib.Data.Sym.Sym2.Init -import Mathlib.Tactic.NormNum.GCD - -deprecated_module (since := "2025-12-13") diff --git a/Mathlib/Algebra/Lie/Cochain.lean b/Mathlib/Algebra/Lie/Cochain.lean index b51e93210a2d31..4370193d343ebc 100644 --- a/Mathlib/Algebra/Lie/Cochain.lean +++ b/Mathlib/Algebra/Lie/Cochain.lean @@ -9,6 +9,7 @@ public import Mathlib.Algebra.Lie.Abelian /-! # Lie algebra cohomology in low degree + This file defines low degree cochains of Lie algebras with coefficients given by a module. They are useful in the construction of central extensions, so we treat these easier cases separately from the general theory of Lie algebra cohomology. @@ -117,26 +118,20 @@ lemma d₁₂_apply_apply_ofTrivial [LieModule.IsTrivial L M] (f : oneCochain R d₁₂ R L M f x y = - f ⁅x, y⁆ := by simp [trivial_lie_zero] -set_option backward.privateInPublic true in /-- The coboundary operator taking degree 2 cochains to a space containing degree 3 cochains. -/ -private def d₂₃Aux (a : twoCochain R L M) : L →ₗ[R] L →ₗ[R] L →ₗ[R] M where - toFun x := - { toFun y := - { toFun z := ⁅x, a y z⁆ - ⁅y, a x z⁆ + ⁅z, a x y⁆ - a ⁅x, y⁆ z + a ⁅x, z⁆ y - a ⁅y, z⁆ x - map_add' _ _ := by simp; abel - map_smul' _ _ := by abel_nf; simp } +def d₂₃ : twoCochain R L M →ₗ[R] L →ₗ[R] L →ₗ[R] L →ₗ[R] M where + toFun a := { + toFun x := { + toFun y := { + toFun z := ⁅x, a y z⁆ - ⁅y, a x z⁆ + ⁅z, a x y⁆ - a ⁅x, y⁆ z + a ⁅x, z⁆ y - a ⁅y, z⁆ x + map_add' _ _ := by simp; abel + map_smul' _ _ := by simp; abel_nf; simp } map_add' _ _ := by ext; simp; abel - map_smul' _ _ := by ext; abel_nf; simp } + map_smul' _ _ := by ext; simp; abel_nf; simp } + map_add' _ _ := by ext; simp; abel + map_smul' _ _ := by ext; simp; abel_nf; simp } map_add' _ _ := by ext; simp; abel - map_smul' _ _ := by ext; abel_nf; simp - -set_option backward.privateInPublic true in -set_option backward.privateInPublic.warn false in -/-- The coboundary operator taking degree 2 cochains to a space containing degree 3 cochains. -/ -def d₂₃ : twoCochain R L M →ₗ[R] L →ₗ[R] L →ₗ[R] L →ₗ[R] M where - toFun := d₂₃Aux R L M - map_add' _ _ := by ext; simp [d₂₃Aux]; abel - map_smul' _ _ := by ext; simp [d₂₃Aux]; abel_nf; simp + map_smul' _ _ := by ext; simp; abel_nf; simp @[simp] lemma d₂₃_apply (a : twoCochain R L M) (x y z : L) : diff --git a/Mathlib/Algebra/Lie/Extension.lean b/Mathlib/Algebra/Lie/Extension.lean index d4de489559e2ba..54f1fa462d7f6a 100644 --- a/Mathlib/Algebra/Lie/Extension.lean +++ b/Mathlib/Algebra/Lie/Extension.lean @@ -10,6 +10,7 @@ public import Mathlib.Algebra.Lie.Cochain /-! # Extensions of Lie algebras + This file defines extensions of Lie algebras, given by short exact sequences of Lie algebra homomorphisms. They are implemented in two ways: `IsExtension` is a `Prop`-valued class taking two homomorphisms as parameters, and `Extension` is a structure that includes the middle Lie algebra. diff --git a/Mathlib/Algebra/Lie/Killing.lean b/Mathlib/Algebra/Lie/Killing.lean index 6e24e0b900a1be..58a0a842e07fab 100644 --- a/Mathlib/Algebra/Lie/Killing.lean +++ b/Mathlib/Algebra/Lie/Killing.lean @@ -13,9 +13,9 @@ public import Mathlib.Algebra.Lie.TraceForm # Lie algebras with non-degenerate Killing forms. In characteristic zero, the following three conditions are equivalent: - 1. The solvable radical of a Lie algebra is trivial - 2. A Lie algebra is a direct sum of its simple ideals - 3. A Lie algebra has non-degenerate Killing form +1. The solvable radical of a Lie algebra is trivial +2. A Lie algebra is a direct sum of its simple ideals +3. A Lie algebra has non-degenerate Killing form In positive characteristic, it is still true that 3 implies 2, and that 2 implies 1, but there are counterexamples to the remaining implications. Thus condition 3 is the strongest assumption. diff --git a/Mathlib/Algebra/Lie/Loop.lean b/Mathlib/Algebra/Lie/Loop.lean index cf87330901b7ed..071293eeccbd6a 100644 --- a/Mathlib/Algebra/Lie/Loop.lean +++ b/Mathlib/Algebra/Lie/Loop.lean @@ -12,6 +12,7 @@ public import Mathlib.Algebra.Polynomial.Laurent /-! # Loop Lie algebras and their central extensions + Given a Lie algebra `L`, the loop algebra is the Lie algebra of maps from a circle into `L`. This can mean many different things, e.g., continuous maps, smooth maps, polynomial maps. In this file, we consider the simplest case of polynomial maps, meaning we take a base change with the ring of diff --git a/Mathlib/Algebra/Lie/Prod.lean b/Mathlib/Algebra/Lie/Prod.lean index 48e9844cd6766e..ad29ba43b02a3e 100644 --- a/Mathlib/Algebra/Lie/Prod.lean +++ b/Mathlib/Algebra/Lie/Prod.lean @@ -25,8 +25,8 @@ This file defines the Lie algebra structure the Product of two Lie algebras - `LieHom.prodMap` the `Prod.map` of two Lie algebra homomorphisms is a Lie algebra homomorphism. ## Todo: Extend to further functionality from LinearMap.prod e.g. - - Lie Equivalences related to products - - Lie Submodule statements +- Lie Equivalences related to products +- Lie Submodule statements -/ @@ -132,7 +132,7 @@ variable (R L₁ L₂) theorem range_inl : range (inl R L₁ L₂) = ker (snd R L₁ L₂) := by rw [← LieSubalgebra.toSubmodule_inj, range_toSubmodule, LieIdeal.toLieSubalgebra_toSubmodule, - ker_toSubmodule] + ker_toSubmodule] exact LinearMap.range_inl R L₁ L₂ theorem ker_snd : ker (snd R L₁ L₂) = range (inl R L₁ L₂) := @@ -140,7 +140,7 @@ theorem ker_snd : ker (snd R L₁ L₂) = range (inl R L₁ L₂) := theorem range_inr : range (inr R L₁ L₂) = ker (fst R L₁ L₂) := by rw [← LieSubalgebra.toSubmodule_inj, range_toSubmodule, LieIdeal.toLieSubalgebra_toSubmodule, - ker_toSubmodule] + ker_toSubmodule] exact LinearMap.range_inr R L₁ L₂ theorem ker_fst : ker (fst R L₁ L₂) = range (inr R L₁ L₂) := diff --git a/Mathlib/Algebra/Lie/UniversalEnveloping.lean b/Mathlib/Algebra/Lie/UniversalEnveloping.lean index dcdf160ac70a4e..8ebf54ba4ef5fa 100644 --- a/Mathlib/Algebra/Lie/UniversalEnveloping.lean +++ b/Mathlib/Algebra/Lie/UniversalEnveloping.lean @@ -6,7 +6,6 @@ Authors: Oliver Nash module public import Mathlib.Algebra.Lie.OfAssociative -public import Mathlib.Algebra.RingQuot public import Mathlib.LinearAlgebra.TensorAlgebra.Basic /-! @@ -57,11 +56,13 @@ so that our construction needs only the semiring structure of the tensor algebra inductive Rel : TensorAlgebra R L → TensorAlgebra R L → Prop | lie_compat (x y : L) : Rel (ιₜ ⁅x, y⁆ + ιₜ y * ιₜ x) (ιₜ x * ιₜ y) +/-- `Rel` as a ring congruence, used to build the quotient. -/ +@[no_expose] def ringCon : RingCon (TensorAlgebra R L) := ringConGen (Rel R L) + end UniversalEnvelopingAlgebra /-- The universal enveloping algebra of a Lie algebra. -/ -def UniversalEnvelopingAlgebra := - RingQuot (UniversalEnvelopingAlgebra.Rel R L) +def UniversalEnvelopingAlgebra := (UniversalEnvelopingAlgebra.ringCon R L).Quotient deriving Inhabited, Ring, Algebra R namespace UniversalEnvelopingAlgebra @@ -69,7 +70,7 @@ namespace UniversalEnvelopingAlgebra /-- The quotient map from the tensor algebra to the universal enveloping algebra as a morphism of associative algebras. -/ def mkAlgHom : TensorAlgebra R L →ₐ[R] UniversalEnvelopingAlgebra R L := - RingQuot.mkAlgHom R (Rel R L) + RingCon.mkₐ R _ variable {L} attribute [local instance 100] LieRing.ofAssociativeRing @@ -81,7 +82,7 @@ def ι : L →ₗ⁅R⁆ UniversalEnvelopingAlgebra R L := map_lie' := fun {x y} => by suffices mkAlgHom R L (ιₜ ⁅x, y⁆ + ιₜ y * ιₜ x) = mkAlgHom R L (ιₜ x * ιₜ y) by rw [map_mul] at this; simp [LieRing.of_associative_ring_bracket, ← this] - exact RingQuot.mkAlgHom_rel _ (Rel.lie_compat x y) } + exact Quotient.sound <| RingCon.le_ringConGen _ _ (Rel.lie_compat x y) } variable {A : Type u₃} [Ring A] [Algebra R A] (f : L →ₗ⁅R⁆ A) @@ -90,11 +91,11 @@ set_option backward.isDefEq.respectTransparency false in associative algebras lift to associative algebra morphisms from the universal enveloping algebra. -/ def lift : (L →ₗ⁅R⁆ A) ≃ (UniversalEnvelopingAlgebra R L →ₐ[R] A) where toFun f := - RingQuot.liftAlgHom R - ⟨TensorAlgebra.lift R (f : L →ₗ[R] A), by + RingCon.liftₐ _ + (TensorAlgebra.lift R (f : L →ₗ[R] A)) <| by + grw [ringCon, RingCon.ringConGen_le] intro a b h; induction h - simp only [LieRing.of_associative_ring_bracket, map_add, TensorAlgebra.lift_ι_apply, - LieHom.coe_toLinearMap, LieHom.map_lie, map_mul, sub_add_cancel]⟩ + simp [LieRing.of_associative_ring_bracket] invFun F := (F : UniversalEnvelopingAlgebra R L →ₗ⁅R⁆ A).comp (ι R) left_inv f := by ext @@ -105,10 +106,9 @@ def lift : (L →ₗ⁅R⁆ A) ≃ (UniversalEnvelopingAlgebra R L →ₐ[R] A) -- RingQuot.liftAlgHom_mkAlgHom_apply] simp only [LieHom.coe_comp, Function.comp_apply, AlgHom.coe_toLieHom, UniversalEnvelopingAlgebra.ι_apply, mkAlgHom] - simp only [UniversalEnvelopingAlgebra, RingQuot.liftAlgHom_mkAlgHom_apply, - TensorAlgebra.lift_ι_apply, LieHom.coe_toLinearMap] + simp [UniversalEnvelopingAlgebra] right_inv F := by - apply RingQuot.ringQuot_ext' + apply RingCon.Quotient.hom_extₐ ext -- Porting note: was -- simp only [ι, mkAlgHom, TensorAlgebra.lift_ι_apply, LieHom.coe_toLinearMap, diff --git a/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean b/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean index e9150dd43eb190..063ea6e14fe236 100644 --- a/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean +++ b/Mathlib/Algebra/LieRinehartAlgebra/Defs.lean @@ -45,8 +45,6 @@ class LieRinehartAlgebra (R A L : Type*) [CommRing A] [LieRing L] [CommRing R] [Algebra R A] [LieAlgebra R L] : Prop extends IsScalarTower R A L, LieModule R L A -namespace LieRinehartAlgebra - variable {R A₁ L₁ A₂ L₂ A₃ L₃ : Type*} [CommRing R] [CommRing A₁] [LieRing L₁] [Module A₁ L₁] [LieRingModule L₁ A₁] [CommRing A₂] [LieRing L₂] [Module A₂ L₂] [LieRingModule L₂ A₂] @@ -72,6 +70,8 @@ instance : LieRinehartRing A₁ (Derivation R A₁ A₁) where /-- The derivations of a commutative Algebra themselves form a LieRinehart-Algebra. -/ instance : LieRinehartAlgebra R A₁ (Derivation R A₁ A₁) where +namespace LieRinehartAlgebra + /-- A morphism of Lie-Rinehart algebras, from `(A₁, L₁)` to `(A₂, L₂)`, consists of a pair of maps `(σ, F)` where `σ : A₁ → A₂` is a morphism of algebras and `F` is a morphism of Lie algebras, which respect the module structures. diff --git a/Mathlib/Algebra/LieRinehartAlgebra/Subalgebra.lean b/Mathlib/Algebra/LieRinehartAlgebra/Subalgebra.lean new file mode 100644 index 00000000000000..3139fea5998468 --- /dev/null +++ b/Mathlib/Algebra/LieRinehartAlgebra/Subalgebra.lean @@ -0,0 +1,232 @@ +/- +Copyright (c) 2026 Leonid Ryvkin. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Leonid Ryvkin +-/ + +module + +public import Mathlib.Algebra.LieRinehartAlgebra.Defs + +/-! +# Lie-Rinehart subalgebras + +This file defines Lie-Rinehart subalgebras of a Lie-Rinehart algebra and provides basic related +definitions and results. + +## Main definitions/ statements: + +* `LieRinehartSubalgebra` as an `A`-submodule of `L` stable under the Lie bracket. (This is also +applicable to Lie-Rinehart rings and more generally any `A`-module with a Lie ring structure). + +* A Lie-Rinehart subalgebra of a Lie-Rinehart ring is a Lie-Rinehart ring + +* A Lie-Rinehart subalgebra of a Lie-Rinehart algebra is a Lie-Rinehart algebra over the same ring. + +-/ + +public section + +open scoped LieRinehartAlgebra + +variable (A L : Type*) [CommRing A] [LieRing L] [Module A L] + +/-- A Lie-Rinehart subalgebra of a Lie-Rinehart algebra `(R A L)` is an `A`-submodule of `L`, which +is stable under the Lie bracket. (This can be defined independently of `R` and most +Lie-Rinehart algebra axioms). -/ +structure LieRinehartSubalgebra extends Submodule A L where + lie_mem' {a b} : a ∈ carrier → b ∈ carrier → ⁅a, b⁆ ∈ carrier + +instance : Zero (LieRinehartSubalgebra A L) := + ⟨⟨0, fun {x y hx _hy} ↦ by simp [(Submodule.mem_bot A).mp hx]⟩⟩ + +instance : Inhabited (LieRinehartSubalgebra A L) := + ⟨0⟩ + +namespace LieRinehartSubalgebra + +instance : SetLike (LieRinehartSubalgebra A L) L where + coe L' := L'.carrier + coe_injective L' L'' h := by + rcases L' + rcases L'' + congr + exact SetLike.coe_injective h + +instance : PartialOrder (LieRinehartSubalgebra A L) := .ofSetLike (LieRinehartSubalgebra A L) L + +instance : AddSubgroupClass (LieRinehartSubalgebra A L) L where + add_mem := Submodule.add_mem _ + zero_mem L' := L'.zero_mem' + neg_mem {L'} x hx := show -x ∈ L'.toSubmodule from neg_mem hx + +instance : SMulMemClass (LieRinehartSubalgebra A L) A L where + smul_mem {s} := SMulMemClass.smul_mem (s := s.toSubmodule) + +/-- A Lie-Rinehart subalgebra forms a Lie ring. -/ +instance lieRing (L' : LieRinehartSubalgebra A L) : LieRing L' where + bracket x y := ⟨⁅x.val, y.val⁆, L'.lie_mem' x.property y.property⟩ + lie_add x y z := by aesop + add_lie x y z := by aesop + lie_self x := by aesop + leibniz_lie x y z := by aesop + +variable {A L} +variable (L' : LieRinehartSubalgebra A L) + +protected theorem zero_mem : (0 : L) ∈ L' := + zero_mem L' + +protected theorem add_mem {x y : L} : x ∈ L' → y ∈ L' → (x + y : L) ∈ L' := + add_mem + +protected theorem sub_mem {x y : L} : x ∈ L' → y ∈ L' → (x - y : L) ∈ L' := + sub_mem + +protected theorem smul_mem (t : A) {x : L} (h : x ∈ L') : t • x ∈ L' := + SMulMemClass.smul_mem _ h + +theorem lie_mem {x y : L} (hx : x ∈ L') (hy : y ∈ L') : (⁅x, y⁆ : L) ∈ L' := + L'.lie_mem' hx hy + +theorem mem_carrier {x : L} : x ∈ L'.carrier ↔ x ∈ (L' : Set L) := + Iff.rfl + +theorem mem_mk_iff (S : Set L) (h₁ h₂ h₃ h₄) {x : L} : + x ∈ (⟨⟨⟨⟨S, h₁⟩, h₂⟩, h₃⟩, h₄⟩ : LieRinehartSubalgebra A L) ↔ x ∈ S := + Iff.rfl + +@[simp] +theorem mem_toSubmodule {x : L} : x ∈ L'.toSubmodule ↔ x ∈ L' := + Iff.rfl + +@[simp] +theorem mem_mk_iff' (p : Submodule A L) (h) {x : L} : + x ∈ (⟨p, h⟩ : LieRinehartSubalgebra A L) ↔ x ∈ p := + Iff.rfl + +theorem mem_coe {x : L} : x ∈ (L' : Set L) ↔ x ∈ L' := + Iff.rfl + +@[simp, norm_cast] +theorem coe_bracket (x y : L') : (↑⁅x, y⁆ : L) = ⁅(↑x : L), ↑y⁆ := + rfl + +theorem ext_iff (x y : L') : x = y ↔ (x : L) = y := Subtype.ext_iff + +theorem coe_zero_iff_zero (x : L') : (x : L) = 0 ↔ x = 0 := (ext_iff L' x 0).symm + +@[ext] +theorem ext (L₁' L₂' : LieRinehartSubalgebra A L) (h : ∀ x, x ∈ L₁' ↔ x ∈ L₂') : L₁' = L₂' := + SetLike.ext h + +theorem ext_iff' (L₁' L₂' : LieRinehartSubalgebra A L) : L₁' = L₂' ↔ ∀ x, x ∈ L₁' ↔ x ∈ L₂' := + SetLike.ext_iff + +@[simp] +theorem mk_coe (S : Set L) (h₁ h₂ h₃ h₄) : + ((⟨⟨⟨⟨S, h₁⟩, h₂⟩, h₃⟩, h₄⟩ : LieRinehartSubalgebra A L) : Set L) = S := + rfl + +theorem toSubmodule_mk (p : Submodule A L) (h) : + ({ p with lie_mem' := h } : LieRinehartSubalgebra A L).toSubmodule = p := rfl + +theorem coe_injective : Function.Injective ((↑) : LieRinehartSubalgebra A L → Set L) := + SetLike.coe_injective + +@[norm_cast] +theorem coe_set_eq (L₁' L₂' : LieRinehartSubalgebra A L) : (L₁' : Set L) = L₂' ↔ L₁' = L₂' := + SetLike.coe_set_eq + +theorem toSubmodule_injective : Function.Injective (toSubmodule (A := A) (L := L)) := by + intro L₁' L₂' h + rw [SetLike.ext'_iff] at h + rw [← coe_set_eq] + exact h + +theorem coe_toSubmodule : (L'.toSubmodule : Set L) = L' := + rfl + +section LieModule + +variable {M : Type*} [AddCommGroup M] [LieRingModule L M] + +instance : Bracket L' M where + bracket x m := ⁅(x : L), m⁆ + +@[simp] +theorem coe_bracket_of_module (x : L') (m : M) : ⁅x, m⁆ = ⁅(x : L), m⁆ := + rfl + +instance : IsLieTower L' L M where + leibniz_lie x y m := leibniz_lie x.val y m + +/-- Given a Lie-Rinehart algebra `L` containing a LieRinehart subalgebra `L' ⊆ L`, together with a +Lie ring module `M` of `L`, we may regard `M` as a Lie ring module of `L'` by restriction. -/ +instance lieRingModule : LieRingModule L' M where + add_lie x y m := add_lie (x : L) y m + lie_add x y m := lie_add (x : L) y m + leibniz_lie x y m := leibniz_lie x (y : L) m + +end LieModule + +variable [LieRingModule L A] [LieRinehartRing A L] + +/-- A Lie-Rinehart subalgebra of a Lie-Rinehart ring forms a new Lie-Rinehart ring. -/ +instance : LieRinehartRing A L' where + lie_smul_eq_mul' a b x := LieRinehartRing.lie_smul_eq_mul a b (x : L) + leibniz_mul_right' x a b := LieRinehartRing.leibniz_mul_right (x : L) a b + leibniz_smul_right' _ _ _ := by simp [ext_iff] + +variable (R : Type*) [CommRing R] [Algebra R A] [LieAlgebra R L] [LieRinehartAlgebra R A L] + +/-- A Lie-Rinehart subalgebra of a Lie-Rinehart algebra forms a Lie algebra. -/ +instance lieAlgebra : LieAlgebra R L' where + lie_smul := by aesop + +/-- Converts a Lie-Rinehart subalgebra to the corresponding Lie subalgebra. -/ +@[expose] def toLieSubalgebra : LieSubalgebra R L where + toSubmodule := L'.toSubmodule.restrictScalars R + lie_mem' := L'.lie_mem' + +theorem toLieSubalgebra_injective : Function.Injective (fun L' => + L'.toLieSubalgebra R : LieRinehartSubalgebra A L → LieSubalgebra R L) := fun L₁' L₂' h ↦ by + rw [SetLike.ext'_iff] at h + rw [← coe_set_eq] + exact h + +@[simp] +theorem toLieSubalgebra_inj (L₁' L₂' : LieRinehartSubalgebra A L) : + (L₁'.toLieSubalgebra R) = (L₂'.toLieSubalgebra R) ↔ L₁' = L₂' := + (toLieSubalgebra_injective R).eq_iff + +theorem coe_toLieSubalgebra : ((L'.toLieSubalgebra R) : Set L) = L' := rfl + +section LieModule + +variable {M : Type*} [AddCommGroup M] [LieRingModule L M] [Module R M] + +/-- Given a Lie-Rinehart algebra `L` containing a LieRinehart subalgebra `L' ⊆ L`, together with a +Lie module `M` of `L`, we may regard `M` as a Lie module of `L'` by restriction. -/ +instance lieModule [LieModule R L M] : LieModule R L' M where + smul_lie t x m := by + rw [coe_bracket_of_module, Submodule.coe_smul_of_tower, smul_lie, coe_bracket_of_module] + lie_smul t x m := by simp only [coe_bracket_of_module, lie_smul] + +end LieModule + +/-- A Lie-Rinehart subalgebra forms a new Lie-Rinehart algebra. -/ +instance : LieRinehartAlgebra R A L' where + +/-- The embedding of a Lie-Rinehart subalgebra into the ambient space as a morphism of +Lie-Rinehart algebras. -/ +@[expose] def incl : L' →ₗ⁅(AlgHom.id R A)⁆ L where + __ := L'.toSubmodule.subtype.restrictScalars R + map_lie' {x y} := coe_bracket L' x y + map_smul_apply' a x := L'.toSubmodule.subtype.map_smul a x + apply_lie' a x := AlgHom.id_apply ⁅x, a⁆ + +@[simp] +theorem coe_incl : ⇑(L'.incl R) = ((↑) : L' → L) := rfl + +end LieRinehartSubalgebra diff --git a/Mathlib/Algebra/LinearRecurrence.lean b/Mathlib/Algebra/LinearRecurrence.lean index 6634b8b7951ff4..cbbc654009610a 100644 --- a/Mathlib/Algebra/LinearRecurrence.lean +++ b/Mathlib/Algebra/LinearRecurrence.lean @@ -8,6 +8,7 @@ module public import Mathlib.Algebra.Polynomial.Degree.Operations public import Mathlib.Algebra.Polynomial.Eval.Defs public import Mathlib.LinearAlgebra.Dimension.Constructions +public import Mathlib.Tactic.CrossRefAttribute /-! # Linear recurrence @@ -48,6 +49,7 @@ open Polynomial /-- A "linear recurrence relation" over a commutative semiring is given by its order `n` and `n` coefficients. -/ +@[wikidata Q364089] structure LinearRecurrence (R : Type*) [CommSemiring R] where /-- Order of the linear recurrence -/ order : ℕ diff --git a/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean b/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean index ec403ae2982f64..c3109ab524ca7b 100644 --- a/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean +++ b/Mathlib/Algebra/Module/LinearMap/FiniteRange.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2026 Patrick Massot. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Patrick Massot, Anatole Dedecker +Authors: Patrick Massot, Anatole Dedecker, Yongxi Lin -/ module @@ -32,6 +32,9 @@ In this file, we define: noetherian ring, in which case the two notions agree. This is an instance in the scope `LinearMap.FiniteRangeSetoid`, so opening this scope allows this relation to be denoted by `≈`. +* `LinearMap.IsQuasiInverse`: two linear maps `u` and `v` are **quasi-inverses** if we have + `u ∘ₗ v ≈ id` and `v ∘ₗ u ≈ id` modulo linear maps with noetherian ranges. + -/ @[expose] public section @@ -50,10 +53,12 @@ variable [Semiring K] [AddCommMonoid V₃] [Module K V₃] /-- A linear map **has Noetherian range** if its range is a Noetherian module. -/ -def HasNoetherianRange (f : V →ₗ[K] V₂) := IsNoetherian K f.range +def HasNoetherianRange (f : V →ₗ[K] V₂) : Prop := + IsNoetherian K f.range /-- A linear map **has finite range** if its range is finitely generated. -/ -def HasFiniteRange (f : V →ₗ[K] V₂) := f.range.FG +def HasFiniteRange (f : V →ₗ[K] V₂) : Prop := + f.range.FG lemma hasNoetherianRange_iff_range {f : V →ₗ[K] V₂} : f.HasNoetherianRange ↔ IsNoetherian K f.range := @@ -230,6 +235,13 @@ lemma equiv_iff_hasFiniteRange [IsNoetherianRing K] {u v : V →ₗ[K] V₂} : u ≈ v ↔ (u - v).HasFiniteRange := by rw [equiv_iff_hasNoetherianRange, hasNoetherianRange_iff_hasFiniteRange] +lemma equiv_zero_iff_hasNoetherianRange {u : V →ₗ[K] V₂} : u ≈ 0 ↔ u.HasNoetherianRange := by + simp [equiv_iff_hasNoetherianRange] + +lemma equiv_zero_iff_hasFiniteRange [IsNoetherianRing K] {u : V →ₗ[K] V₂} : + u ≈ 0 ↔ u.HasFiniteRange := by + simp [equiv_iff_hasFiniteRange] + lemma equiv_iff_isNoetherian_quotient_eqLocus {u v : V →ₗ[K] V₂} : u ≈ v ↔ IsNoetherian K (V ⧸ eqLocus u v) := by rw [equiv_iff_hasNoetherianRange, hasNoetherianRange_iff_quotient_ker, eqLocus_eq_ker_sub] @@ -264,8 +276,197 @@ lemma equiv_comp {u v : V →ₗ[K] V₂} {u' v' : V₂ →ₗ[K] V₃} (h : u u' ∘ₗ u ≈ v' ∘ₗ v := by grw [equiv_comp_right h', equiv_comp_left h] +lemma projection_equiv_zero_iff_isNoetherian {S T : Submodule K V} (hST : IsCompl S T) : + S.projection T hST ≈ 0 ↔ IsNoetherian K S := by + rw [equiv_zero_iff_hasNoetherianRange, hasNoetherianRange_iff_range, range_projection] + +lemma projection_equiv_zero {S T : Submodule K V} [IsNoetherian K S] (hST : IsCompl S T) : + S.projection T hST ≈ 0 := + projection_equiv_zero_iff_isNoetherian hST |>.mpr inferInstance + +lemma projection_equiv_id_iff_isNoetherian {S T : Submodule K V} (hST : IsCompl S T) : + S.projection T hST ≈ id ↔ IsNoetherian K T := by + rw [Setoid.comm, equiv_iff_hasNoetherianRange, ← projection_eq_id_sub_projection, + hasNoetherianRange_iff_range, range_projection] + +lemma projection_equiv_id {S T : Submodule K V} [IsNoetherian K T] (hST : IsCompl S T) : + S.projection T hST ≈ id := + projection_equiv_id_iff_isNoetherian hST |>.mpr inferInstance + end FiniteRangeSetoid end Setoid +section QuasiInverse + +variable [CommRing K] + [AddCommGroup V] [Module K V] + [AddCommGroup V₂] [Module K V₂] + [AddCommGroup V₃] [Module K V₃] + +open scoped LinearMap.FiniteRangeSetoid + +/-- `u` is a **left quasi-inverse** to `v` if `u ∘ₗ v ≈ id` modulo +linear maps with noetherian ranges. Recall that if the scalar ring is noetherian +(e.g a field), then "noetherian range" can be replaced by "finitely generated range". -/ +def IsLeftQuasiInverse (u : V →ₗ[K] V₂) (v : V₂ →ₗ[K] V) : Prop := + u ∘ₗ v ≈ .id + +/-- `u` is a **right quasi-inverse** to `v` if `v ∘ₗ u ≈ id` modulo +linear maps with noetherian ranges. Recall that if the scalar ring is noetherian +(e.g a field), then "noetherian range" can be replaced by "finitely generated range". -/ +def IsRightQuasiInverse (u : V₃ →ₗ[K] V₂) (v : V₂ →ₗ[K] V₃) : Prop := + v ∘ₗ u ≈ .id + +/-- `u` is a **quasi-inverse** to `v` if `u ∘ₗ v ≈ id` and `v ∘ₗ u ≈ id` modulo +linear maps with noetherian ranges. Recall that if the scalar ring is noetherian +(e.g a field), then "noetherian range" can be replaced by "finitely generated range". -/ +def IsQuasiInverse (u : V₃ →ₗ[K] V₂) (v : V₂ →ₗ[K] V₃) : Prop := + u.IsLeftQuasiInverse v ∧ u.IsRightQuasiInverse v + +lemma isLeftQuasiInverse_iff_isRightQuasiInverse_swap {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} : + u.IsLeftQuasiInverse v ↔ v.IsRightQuasiInverse u := Iff.rfl + +alias ⟨IsLeftQuasiInverse.isRightQuasiInverse, IsRightQuasiInverse.isLeftQuasiInverse⟩ := + isLeftQuasiInverse_iff_isRightQuasiInverse_swap + +lemma IsLeftQuasiInverse.equiv {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} + (h : u.IsLeftQuasiInverse v) : u ∘ₗ v ≈ .id := h + +lemma IsRightQuasiInverse.equiv {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} + (h : u.IsRightQuasiInverse v) : v ∘ₗ u ≈ .id := h + +@[symm] +lemma IsQuasiInverse.symm {u : V₃ →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} + (h : u.IsQuasiInverse v) : v.IsQuasiInverse u := + And.symm h + +@[gcongr] +lemma IsLeftQuasiInverse.congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (h : u.IsLeftQuasiInverse v) (hu : u' ≈ u) (hv : v' ≈ v) : + u'.IsLeftQuasiInverse v' := by + unfold IsLeftQuasiInverse at * + grw [hu, hv] + assumption + +@[gcongr] +lemma isLeftQuasiInverse_congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (hu : u' ≈ u) (hv : v' ≈ v) : + u.IsLeftQuasiInverse v ↔ u'.IsLeftQuasiInverse v' := + ⟨fun H ↦ H.congr hu hv, fun H ↦ H.congr (Setoid.symm hu) (Setoid.symm hv)⟩ + +@[gcongr] +lemma IsRightQuasiInverse.congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (h : u.IsRightQuasiInverse v) (hu : u' ≈ u) (hv : v' ≈ v) : + u'.IsRightQuasiInverse v' := + h.isLeftQuasiInverse.congr hv hu |>.isRightQuasiInverse + +lemma isRightQuasiInverse_congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (hu : u' ≈ u) (hv : v' ≈ v) : + u.IsRightQuasiInverse v ↔ u'.IsRightQuasiInverse v' := + ⟨fun H ↦ H.congr hu hv, fun H ↦ H.congr (Setoid.symm hu) (Setoid.symm hv)⟩ + +@[gcongr] +lemma IsQuasiInverse.congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (h : u.IsQuasiInverse v) (hu : u' ≈ u) (hv : v' ≈ v) : + u'.IsQuasiInverse v' := + ⟨h.1.congr hu hv, h.2.congr hu hv⟩ + +lemma isQuasiInverse_congr {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (hu : u' ≈ u) (hv : v' ≈ v) : + u.IsQuasiInverse v ↔ u'.IsQuasiInverse v' := by + simp [IsQuasiInverse, isLeftQuasiInverse_congr hu hv, isRightQuasiInverse_congr hu hv] + +lemma IsQuasiInverse.equiv_of_left {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (h : u.IsQuasiInverse v) (h' : u'.IsQuasiInverse v') (hu : u ≈ u') : + v ≈ v' := by + calc + v = v ∘ₗ .id := by simp + _ ≈ v ∘ₗ (u' ∘ₗ v') := by grw [h'.1.equiv] + _ ≈ v ∘ₗ (u ∘ₗ v') := by grw [hu] + _ = (v ∘ₗ u) ∘ₗ v' := by rw [comp_assoc] + _ ≈ .id ∘ₗ v' := by grw [h.2.equiv] + _ = v' := by simp + +lemma IsQuasiInverse.equiv_of_right {u u' : V₃ →ₗ[K] V₂} {v v' : V₂ →ₗ[K] V₃} + (h : u.IsQuasiInverse v) (h' : u'.IsQuasiInverse v') (hv : v ≈ v') : + u ≈ u' := + h.symm.equiv_of_left h'.symm hv + +/-- Left quasi-inverses compose in the opposite order. -/ +lemma IsLeftQuasiInverse.comp {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} {u' : V₂ →ₗ[K] V} + {v' : V₃ →ₗ[K] V₂} (hu : u'.IsLeftQuasiInverse u) (hv : v'.IsLeftQuasiInverse v) : + (u' ∘ₗ v').IsLeftQuasiInverse (v ∘ₗ u) := + calc + _ = u' ∘ₗ (v' ∘ₗ v) ∘ₗ u := rfl + _ ≈ u' ∘ₗ .id ∘ₗ u := by grw [hv.equiv] + _ ≈ .id := hu.equiv + +/-- Right quasi-inverses compose in the opposite order. -/ +lemma IsRightQuasiInverse.comp {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} {u' : V₂ →ₗ[K] V} + {v' : V₃ →ₗ[K] V₂} (hu : u'.IsRightQuasiInverse u) (hv : v'.IsRightQuasiInverse v) : + (u' ∘ₗ v').IsRightQuasiInverse (v ∘ₗ u) := + hv.isLeftQuasiInverse.comp hu.isLeftQuasiInverse |>.isRightQuasiInverse + +/-- Quasi-inverses compose in the opposite order. -/ +lemma IsQuasiInverse.comp {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} {u' : V₂ →ₗ[K] V} + {v' : V₃ →ₗ[K] V₂} (hu : u'.IsQuasiInverse u) (hv : v'.IsQuasiInverse v) : + (u' ∘ₗ v').IsQuasiInverse (v ∘ₗ u) := + ⟨hu.1.comp hv.1, hu.2.comp hv.2⟩ + +/-- If `u'` is a right quasi-inverse of `u` and `w` is a left quasi-inverse of `v ∘ₗ u`, +then `u ∘ₗ w` is a left quasi-inverse of `v`. -/ +lemma IsLeftQuasiInverse.of_comp_left {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} + {u' : V₂ →ₗ[K] V} {w : V₃ →ₗ[K] V} (hu : u'.IsRightQuasiInverse u) + (hw : w.IsLeftQuasiInverse (v ∘ₗ u)) : + (u ∘ₗ w).IsLeftQuasiInverse v := by + calc + _ = ((u ∘ₗ w) ∘ₗ v) ∘ₗ .id := rfl + _ ≈ ((u ∘ₗ w) ∘ₗ v) ∘ₗ (u ∘ₗ u') := by grw [hu.equiv] + _ = u ∘ₗ (w ∘ₗ (v ∘ₗ u)) ∘ₗ u' := rfl + _ ≈ u ∘ₗ .id ∘ₗ u' := by grw [hw.equiv] + _ ≈ .id := hu.equiv + +/-- If `u'` is a quasi-inverse of `u` and `w` is a quasi-inverse of `v ∘ₗ u`, then +`u ∘ₗ w` is a quasi-inverse of `v`. -/ +lemma IsQuasiInverse.of_comp_left {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} + {u' : V₂ →ₗ[K] V} {w : V₃ →ₗ[K] V} (hu : u'.IsQuasiInverse u) + (hw : w.IsQuasiInverse (v ∘ₗ u)) : + (u ∘ₗ w).IsQuasiInverse v := + ⟨.of_comp_left hu.2 hw.1, hw.2⟩ + +/-- If `v'` is a left quasi-inverse of `v` and `w` is a right quasi-inverse of `v ∘ₗ u`, +then `w ∘ₗ v` is a right quasi-inverse of `u`. -/ +lemma IsRightQuasiInverse.of_comp_right {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} + {v' : V₃ →ₗ[K] V₂} {w : V₃ →ₗ[K] V} (hv : v'.IsLeftQuasiInverse v) + (hw : w.IsRightQuasiInverse (v ∘ₗ u)) : + (w ∘ₗ v).IsRightQuasiInverse u := by + calc + _ = .id ∘ₗ (u ∘ₗ (w ∘ₗ v)) := rfl + _ ≈ (v' ∘ₗ v) ∘ₗ (u ∘ₗ (w ∘ₗ v)) := by grw [hv.equiv] + _ = v' ∘ₗ ((v ∘ₗ u) ∘ₗ w) ∘ₗ v := rfl + _ ≈ v' ∘ₗ .id ∘ₗ v := by grw [hw.equiv] + _ ≈ .id := hv.equiv + +/-- If `v'` is a quasi-inverse of `v` and `w` is a quasi-inverse of `v ∘ₗ u`, then +`w ∘ₗ v` is a quasi-inverse of `u`. -/ +lemma IsQuasiInverse.of_comp_right {u : V →ₗ[K] V₂} {v : V₂ →ₗ[K] V₃} + {v' : V₃ →ₗ[K] V₂} {w : V₃ →ₗ[K] V} (hv : v'.IsQuasiInverse v) + (hw : w.IsQuasiInverse (v ∘ₗ u)) : + (w ∘ₗ v).IsQuasiInverse u := + ⟨hw.1, IsRightQuasiInverse.of_comp_right hv.1 hw.2⟩ + +lemma isQuasiInverse_subtype_projectionOnto_iff {S T : Submodule K V} (hST : IsCompl S T) : + IsQuasiInverse S.subtype (S.projectionOnto T hST) ↔ IsNoetherian K T := by + rw [IsQuasiInverse, and_iff_left (by simp [IsRightQuasiInverse, projectionOnto_comp_subtype]), + IsLeftQuasiInverse, ← projection, + FiniteRangeSetoid.projection_equiv_id_iff_isNoetherian hST] + +lemma isQuasiInverse_subtype_projectionOnto {S T : Submodule K V} [IsNoetherian K T] + (hST : IsCompl S T) : + IsQuasiInverse S.subtype (S.projectionOnto T hST) := + isQuasiInverse_subtype_projectionOnto_iff hST |>.mpr inferInstance + +end QuasiInverse + end LinearMap diff --git a/Mathlib/Algebra/Module/LocalizedModule/Away.lean b/Mathlib/Algebra/Module/LocalizedModule/Away.lean index 1513c0b071c8c5..9d1f97c89ee111 100644 --- a/Mathlib/Algebra/Module/LocalizedModule/Away.lean +++ b/Mathlib/Algebra/Module/LocalizedModule/Away.lean @@ -1,5 +1,81 @@ -module -- shake: keep-all +/- +Copyright (c) 2026 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Merten +-/ +module public import Mathlib.Algebra.Module.LocalizedModule.Basic -deprecated_module (since := "2026-04-23") +/-! +# API for localized modules away from an element + +We provide some specialized API for the localization of a module away from an element. +-/ + +public section + +namespace IsLocalizedModule.Away + +variable {R : Type*} [CommSemiring R] {M N : Type*} [AddCommMonoid M] [AddCommMonoid N] + [Module R M] [Module R N] {f : M →ₗ[R] N} {r : R} + +lemma mk (h₁ : IsUnit (algebraMap R (Module.End R N) r)) + (h₂ : ∀ (x : N), ∃ (n : ℕ) (y : M), r ^ n • x = f y) + (h₃ : ∀ (x y : M), f x = f y → ∃ (n : ℕ), r ^ n • x = r ^ n • y) : + IsLocalizedModule.Away r f where + map_units := fun ⟨_, ⟨n, rfl⟩⟩ ↦ by simp [h₁.pow] + surj x := by + obtain ⟨n, y, hy⟩ := h₂ x + use ⟨y, ⟨_, n, rfl⟩⟩, hy + exists_of_eq {x y} hxy := by + obtain ⟨n, hn⟩ := h₃ _ _ hxy + use ⟨_, n, rfl⟩, hn + +lemma mk_of_addCommGroup {M N : Type*} [AddCommGroup M] [AddCommGroup N] [Module R M] [Module R N] + {f : M →ₗ[R] N} {r : R} (h₁ : IsUnit (algebraMap R (Module.End R N) r)) + (h₂ : ∀ (x : N), ∃ (n : ℕ) (y : M), r ^ n • x = f y) + (h₃ : ∀ (x : M), f x = 0 → ∃ (n : ℕ), r ^ n • x = 0) : + IsLocalizedModule.Away r f := by + refine IsLocalizedModule.Away.mk h₁ h₂ fun x y hxy ↦ ?_ + have : f (x - y) = 0 := by simp [hxy] + obtain ⟨n, hn⟩ := h₃ _ this + use n + simpa [smul_sub, sub_eq_zero] using hn + +variable (r) [IsLocalizedModule.Away r f] + +variable (f) in +include f in +lemma isUnit_algebraMap : IsUnit (algebraMap R (Module.End R N) r) := + IsLocalizedModule.map_units (S := .powers r) f ⟨_, 1, by simp⟩ + +lemma exists_of_eq {x y : M} (h : f x = f y) : ∃ (n : ℕ), r ^ n • x = r ^ n • y := by + obtain ⟨⟨_, n, rfl⟩, hn⟩ := IsLocalizedModule.exists_of_eq (S := .powers r) h + use n, hn + +variable (f) in +lemma surj (y : N) : ∃ (n : ℕ) (x : M), r ^ n • y = f x := by + obtain ⟨⟨x, ⟨_, n, rfl⟩⟩, h⟩ := IsLocalizedModule.surj (S := .powers r) f y + use n, x, h + +lemma of_associated {r r' : R} (h : Associated r r') [IsLocalizedModule.Away r f] : + IsLocalizedModule.Away r' f := by + obtain ⟨u, rfl⟩ := h + rw [mul_comm] + refine .mk ?_ ?_ ?_ + · simp [IsUnit.mul, isUnit_algebraMap f r, u.isUnit.map _] + · intro y + obtain ⟨n, x, hx⟩ := surj f r y + use n, (u ^ n) • x + simp [mul_pow, ← hx, mul_smul, Units.smul_def] + · intro x y hxy + obtain ⟨n, hn⟩ := exists_of_eq r hxy + use n + simp [mul_pow, mul_smul, hn] + +lemma iff_of_associated {r r' : R} (h : Associated r r') : + IsLocalizedModule.Away r f ↔ IsLocalizedModule.Away r' f := + ⟨fun _ ↦ .of_associated h, fun _ ↦ .of_associated h.symm⟩ + +end IsLocalizedModule.Away diff --git a/Mathlib/Algebra/Module/NatInt.lean b/Mathlib/Algebra/Module/NatInt.lean index 9daa84aa1c71eb..80968cbeb8c4a2 100644 --- a/Mathlib/Algebra/Module/NatInt.lean +++ b/Mathlib/Algebra/Module/NatInt.lean @@ -90,17 +90,18 @@ variable (R) in structure. See note [reducible non-instances]. -/ abbrev Module.addCommMonoidToAddCommGroup - [Ring R] [AddCommMonoid M] [Module R M] : AddCommGroup M := - { (inferInstance : AddCommMonoid M) with - neg := fun a => (-1 : R) • a - neg_add_cancel := fun a => - show (-1 : R) • a + a = 0 by - nth_rw 2 [← one_smul R a] - rw [← add_smul, neg_add_cancel, zero_smul] - zsmul := fun z a => (z : R) • a - zsmul_zero' := fun a => by simpa only [Int.cast_zero] using zero_smul R a - zsmul_succ' := fun z a => by simp [add_comm, add_smul] - zsmul_neg' := fun z a => by simp [← smul_assoc] } + [Ring R] [AddCommMonoid M] [Module R M] : AddCommGroup M where + neg := fun a => (-1 : R) • a + neg_add_cancel := fun a => + show (-1 : R) • a + a = 0 by + nth_rw 2 [← one_smul R a] + rw [← add_smul, neg_add_cancel, zero_smul] + zsmul z a := (z : R) • a + zsmul_zero' a := by simp_rw [HSMul.hSMul, SMul.smul, Int.cast_zero]; exact zero_smul R a + zsmul_succ' z a := by simp_rw [HSMul.hSMul, SMul.smul]; simp [add_comm, add_smul] + zsmul_neg' z a := by + change (Int.negSucc z : R) • a = -1 • ((z.succ : ℤ) : R) • a + simp [← smul_assoc] section AddCommMonoid diff --git a/Mathlib/Algebra/Module/Presentation/Basic.lean b/Mathlib/Algebra/Module/Presentation/Basic.lean index c66b20bdc5641d..4f372e7e6ee899 100644 --- a/Mathlib/Algebra/Module/Presentation/Basic.lean +++ b/Mathlib/Algebra/Module/Presentation/Basic.lean @@ -48,10 +48,10 @@ namespace Module variable (A : Type u) [Ring A] +set_option linter.checkUnivs false in /-- Given a ring `A`, this structure involves a family of elements (indexed by a type `R`) in a free module `G →₀ A`. This allows to define an `A`-module by generators and relations, see `Relations.Quotient`. -/ -@[nolint checkUnivs] structure Relations where /-- the index type for generators -/ G : Type w₀ @@ -488,9 +488,9 @@ end Relations variable (M : Type v) [AddCommGroup M] [Module A M] +set_option linter.checkUnivs false in /-- Given an `A`-module `M`, a term in this type is a presentation by `M` by generators and relations. -/ -@[nolint checkUnivs] structure Presentation extends Relations.{w₀, w₁} A, toRelations.Solution M, toSolution.IsPresentation where diff --git a/Mathlib/Algebra/Module/Projective.lean b/Mathlib/Algebra/Module/Projective.lean index 5d944d77a74cbc..c6bd0bb04be016 100644 --- a/Mathlib/Algebra/Module/Projective.lean +++ b/Mathlib/Algebra/Module/Projective.lean @@ -67,6 +67,7 @@ from the free `R`-module on `P` to `P` splits. -/ /-- An R-module is projective if it is a direct summand of a free module, or equivalently if maps from the module lift along surjections. There are several other equivalent definitions. -/ +@[wikidata Q942423] class Module.Projective (R : Type*) [Semiring R] (P : Type*) [AddCommMonoid P] [Module R P] : Prop where out : ∃ s : P →ₗ[R] P →₀ R, Function.LeftInverse (Finsupp.linearCombination R id) s diff --git a/Mathlib/Algebra/Module/Submodule/Ker.lean b/Mathlib/Algebra/Module/Submodule/Ker.lean index 876ecf85efa630..d052a15cceb08c 100644 --- a/Mathlib/Algebra/Module/Submodule/Ker.lean +++ b/Mathlib/Algebra/Module/Submodule/Ker.lean @@ -197,23 +197,21 @@ theorem ker_eq_bot {f : M →ₛₗ[τ₁₂] M₂} : ker f = ⊥ ↔ Injective alias _root_.LinearMapClass.ker_eq_bot := ker_eq_bot @[simp] lemma injective_domRestrict_iff {f : M →ₛₗ[τ₁₂] M₂} {S : Submodule R M} : - Injective (f.domRestrict S) ↔ S ⊓ LinearMap.ker f = ⊥ := by - rw [← LinearMap.ker_eq_bot] - refine ⟨fun h ↦ le_bot_iff.1 ?_, fun h ↦ le_bot_iff.1 ?_⟩ - · intro x ⟨hx, h'x⟩ - have : ⟨x, hx⟩ ∈ LinearMap.ker (LinearMap.domRestrict f S) := by simpa using h'x - rw [h] at this - simpa [mk_eq_zero] using this - · rintro ⟨x, hx⟩ h'x - have : x ∈ S ⊓ LinearMap.ker f := ⟨hx, h'x⟩ - rw [h] at this - simpa [mk_eq_zero] using this - -@[simp] theorem injective_restrict_iff_disjoint {p : Submodule R M} {f : M →ₗ[R] M} - (hf : ∀ x ∈ p, f x ∈ p) : - Injective (f.restrict hf) ↔ Disjoint p (ker f) := by - rw [← ker_eq_bot, ker_restrict hf, ← ker_domRestrict, ker_eq_bot, injective_domRestrict_iff, - disjoint_iff] + Injective (f.domRestrict S) ↔ Disjoint S f.ker := by + simp [← ker_eq_bot, ker_domRestrict, disjoint_iff_comap_eq_bot] + +@[simp] +theorem injective_restrict_iff {p : Submodule R M} {q : Submodule R₂ M₂} {f : M →ₛₗ[τ₁₂] M₂} + (hf : ∀ x ∈ p, f x ∈ q) : Injective (f.restrict hf) ↔ Disjoint p (ker f) := by + simp [← ker_eq_bot, ker_restrict, disjoint_iff_comap_eq_bot] + +@[deprecated (since := "2026-07-01")] +alias injective_restrict_iff_disjoint := injective_restrict_iff + +@[simp] +theorem injective_codRestrict_iff {q : Submodule R₂ M₂} {f : M →ₛₗ[τ₁₂] M₂} + (hf : ∀ x, f x ∈ q) : Injective (f.codRestrict q hf) ↔ Injective f := + Set.injective_codRestrict _ end Ring diff --git a/Mathlib/Algebra/Module/Submodule/Map.lean b/Mathlib/Algebra/Module/Submodule/Map.lean index c41af3f34468ea..2e2f7d8ae98dc0 100644 --- a/Mathlib/Algebra/Module/Submodule/Map.lean +++ b/Mathlib/Algebra/Module/Submodule/Map.lean @@ -245,12 +245,13 @@ theorem comap_inf (f : M →ₛₗ[σ₁₂] M₂) : comap f (q ⊓ q') = comap rfl @[simp] -theorem comap_iInf [RingHomSurjective σ₁₂] {ι : Sort*} (f : M →ₛₗ[σ₁₂] M₂) - (p : ι → Submodule R₂ M₂) : comap f (⨅ i, p i) = ⨅ i, comap f (p i) := - (gc_map_comap f).u_iInf +theorem comap_iInf {ι : Sort*} (f : M →ₛₗ[σ₁₂] M₂) + (p : ι → Submodule R₂ M₂) : comap f (⨅ i, p i) = ⨅ i, comap f (p i) := by + ext + simp @[simp] -theorem comap_finsetInf [RingHomSurjective σ₁₂] {ι : Type*} (f : M →ₛₗ[σ₁₂] M₂) +theorem comap_finsetInf {ι : Type*} (f : M →ₛₗ[σ₁₂] M₂) (s : Finset ι) (p : ι → Submodule R₂ M₂) : comap f (s.inf p) = s.inf fun i ↦ comap f (p i) := by simp [Finset.inf_eq_iInf] @@ -697,8 +698,8 @@ theorem map_restrict [RingHomSurjective σ₂₁] {p : Submodule R₂ M₂} {q : map (f.restrict h) p' = comap q.subtype (map f (map p.subtype p')) := by rw [restrict_eq_codRestrict_domRestrict, map_codRestrict, map_domRestrict] -theorem comap_restrict [RingHomSurjective σ₂₁] {p : Submodule R₂ M₂} {q : Submodule R M} - {f : M₂ →ₛₗ[σ₂₁] M} (h : ∀ x ∈ p, f x ∈ q) (p') : +theorem comap_restrict {p : Submodule R₂ M₂} {q : Submodule R M} {f : M₂ →ₛₗ[σ₂₁] M} + (h : ∀ x ∈ p, f x ∈ q) (p') : comap (f.restrict h) p' = comap p.subtype (comap f (map q.subtype p')) := by rw [restrict_eq_codRestrict_domRestrict, comap_codRestrict, comap_domRestrict] diff --git a/Mathlib/Algebra/Module/Submodule/Range.lean b/Mathlib/Algebra/Module/Submodule/Range.lean index af73d5e971206b..8197d95db2ec94 100644 --- a/Mathlib/Algebra/Module/Submodule/Range.lean +++ b/Mathlib/Algebra/Module/Submodule/Range.lean @@ -114,7 +114,7 @@ theorem range_neg {R : Type*} {R₂ : Type*} {M : Type*} {M₂ : Type*} [Semirin change range ((-LinearMap.id : M₂ →ₗ[R₂] M₂).comp f) = _ rw [range_comp, Submodule.map_neg, Submodule.map_id] -@[simp] lemma range_domRestrict [Module R M₂] (K : Submodule R M) (f : M →ₗ[R] M₂) : +@[simp] lemma range_domRestrict [RingHomSurjective τ₁₂] (K : Submodule R M) (f : M →ₛₗ[τ₁₂] M₂) : range (domRestrict f K) = K.map f := by ext; simp lemma range_domRestrict_le_range [RingHomSurjective τ₁₂] (f : M →ₛₗ[τ₁₂] M₂) (S : Submodule R M) : @@ -303,6 +303,7 @@ open LinearMap @[simp] theorem map_top [RingHomSurjective τ₁₂] (f : M →ₛₗ[τ₁₂] M₂) : map f ⊤ = range f := (range_eq_map f).symm + @[simp] theorem range_subtype : range p.subtype = p := by simpa using map_comap_subtype p ⊤ diff --git a/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean b/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean index 26aca7cdc39ede..a1d249200d4eee 100644 --- a/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean +++ b/Mathlib/Algebra/Module/Submodule/RestrictScalars.lean @@ -56,7 +56,7 @@ theorem restrictScalars_self (V : Submodule R M) : V.restrictScalars R = V := SetLike.coe_injective rfl @[simp] theorem restrictScalars_restrictScalars - (T : Type*) [Semiring T] [SMul T R] [SMul S T] [IsScalarTower S T R] + (T : Type*) [Semiring T] [SMul T R] [SMul S T] [Module T M] [IsScalarTower S T M] [IsScalarTower T R M] (V : Submodule R M) : (V.restrictScalars T).restrictScalars S = V.restrictScalars S := diff --git a/Mathlib/Algebra/Module/Torsion/Basic.lean b/Mathlib/Algebra/Module/Torsion/Basic.lean index 73f3ab0e224fe2..6719c23989257c 100644 --- a/Mathlib/Algebra/Module/Torsion/Basic.lean +++ b/Mathlib/Algebra/Module/Torsion/Basic.lean @@ -577,7 +577,7 @@ theorem IsTorsionBy.mk_smul [(Ideal.span {r}).IsTwoSided] (hM : IsTorsionBy R M def IsTorsionBySet.module [I.IsTwoSided] (hM : IsTorsionBySet R M I) : Module (R ⧸ I) M := letI := hM.hasSMul; fast_instance% I.mkQ_surjective.moduleLeft _ (IsTorsionBySet.mk_smul hM) -instance IsTorsionBySet.isScalarTower [I.IsTwoSided] (hM : IsTorsionBySet R M I) +instance IsTorsionBySet.isScalarTower (hM : IsTorsionBySet R M I) {S : Type*} [SMul S R] [SMul S M] [IsScalarTower S R M] [IsScalarTower S R R] : @IsScalarTower S (R ⧸ I) M _ hM.hasSMul _ := -- Porting note: still needed to be fed the Module R / I M instance diff --git a/Mathlib/Algebra/MonoidAlgebra/Defs.lean b/Mathlib/Algebra/MonoidAlgebra/Defs.lean index f88a130a9fc3c9..8dbf75acb2780e 100644 --- a/Mathlib/Algebra/MonoidAlgebra/Defs.lean +++ b/Mathlib/Algebra/MonoidAlgebra/Defs.lean @@ -156,9 +156,12 @@ lemma ofCoeff_inj {x y : M →₀ R} : ofCoeff x = ofCoeff y ↔ x = y := ofCoef inferInstanceAs <| DecidableEq <| M →₀ R -- TODO: this instance abuses definitional equality with `Finsupp.mapRange` +@[to_additive (dont_translate := A)] +instance {A : Type*} [SMulZeroClass A R] : SMul A R[M] where + smul a x := x.mapRange (a • ·) (smul_zero _) + @[to_additive] instance addCommMonoid : AddCommMonoid R[M] := - fast_instance% { (inferInstance : AddCommMonoid <| M →₀ R) with - nsmul n x := x.mapRange (n • ·) (smul_zero _) } + inferInstanceAs <| AddCommMonoid <| M →₀ R @[to_additive] instance instIsCancelAdd [IsCancelAdd R] : IsCancelAdd R[M] := inferInstanceAs <| IsCancelAdd <| M →₀ R @@ -260,15 +263,13 @@ Further results on scalar multiplication can be found in variable {A : Type*} [SMulZeroClass A R] --- TODO: this instance abuses definitional equality with `Finsupp.mapRange` @[to_additive (dont_translate := A) smulZeroClass] instance smulZeroClass : SMulZeroClass A R[M] := - fast_instance% { (inferInstance : SMulZeroClass A (M →₀ R)) with - smul a x := x.mapRange (a • ·) (smul_zero _) } + inferInstanceAs <| SMulZeroClass A (M →₀ R) section -- Ensure that the different smul instances do not create a diamond. -example : (smulZeroClass (A := ℕ) (R := R) (M := M)).toSMul = addCommMonoid.toNSMul := by +example : (smulZeroClass (A := ℕ) (R := R) (M := M)).smul = addCommMonoid.nsmul := by with_reducible_and_instances rfl -- Ensure that smul has good defeq properties diff --git a/Mathlib/Algebra/MonoidAlgebra/PointwiseSMul.lean b/Mathlib/Algebra/MonoidAlgebra/PointwiseSMul.lean index c62c2e40b5261a..ca705f9ea50a82 100644 --- a/Mathlib/Algebra/MonoidAlgebra/PointwiseSMul.lean +++ b/Mathlib/Algebra/MonoidAlgebra/PointwiseSMul.lean @@ -10,6 +10,7 @@ public import Mathlib.Data.Finset.SMulAntidiagonal /-! # Scalar multiplication by (additive) monoid rings on formal functions. + Given sets `G` and `P`, with a left-cancellative scalar-multiplication (or vector-addition) of `G` on `P`, together with a module `V` over a semiring `R`, we define a convolution action of the monoid algebra `R[G]` on the set of functions `P → V`. diff --git a/Mathlib/Algebra/MvPolynomial/Basic.lean b/Mathlib/Algebra/MvPolynomial/Basic.lean index 5bc4d8845577ea..8c0bd485a58ea7 100644 --- a/Mathlib/Algebra/MvPolynomial/Basic.lean +++ b/Mathlib/Algebra/MvPolynomial/Basic.lean @@ -15,6 +15,7 @@ public import Mathlib.Algebra.Regular.Pow public import Mathlib.Data.Finsupp.Antidiagonal public import Mathlib.Data.Finsupp.Order public import Mathlib.Order.SymmDiff +public meta import Mathlib.Tactic.Polynomial.Core /-! # Multivariate polynomials @@ -106,10 +107,14 @@ def C : R →+* MvPolynomial σ R := variable (R σ) -@[simp] +@[simp, polynomial_post] theorem algebraMap_eq : algebraMap R (MvPolynomial σ R) = C := rfl +@[polynomial_pre] +theorem C_eq_algebraMap : MvPolynomial.C = algebraMap R (MvPolynomial σ R) := + rfl + variable {R σ} @[simp] @@ -593,6 +598,15 @@ theorem coeff_C [DecidableEq σ] (m) (a) : coeff m (C a : MvPolynomial σ R) = if 0 = m then a else 0 := Finsupp.single_apply +theorem coeff_C_of_ne_zero {m : σ →₀ ℕ} (h : m ≠ 0) (a : R) : coeff m (C a) = 0 := by + classical rw [coeff_C, if_neg h.symm] + +-- The intended use case of this theorem is for `n = 1` (often useful for `pderiv`). +@[simp] +theorem coeff_add_single_C {n : ℕ} [NeZero n] {m : σ →₀ ℕ} (a : R) (i : σ) : + coeff (m + Finsupp.single i n) (C a) = 0 := + coeff_C_of_ne_zero (fun H ↦ by simpa [NeZero.ne] using congr($(H) i)) a + lemma eq_C_of_isEmpty [IsEmpty σ] (p : MvPolynomial σ R) : p = C (p.coeff 0) := by obtain ⟨x, rfl⟩ := C_surjective σ p @@ -1069,4 +1083,17 @@ end coeffsIn end CommSemiring +meta section Meta + +open Mathlib.Tactic.Polynomial in +/-- Infer base ring for `MvPolynomial _ R`. Used by the `polynomial` tactic. -/ +@[polynomial_infer_base] +def mvPolynomialInferBaseImpl : PolynomialExt where + infer e := do + match_expr e with + | MvPolynomial _ R _ => pure R + | _ => failure + +end Meta + end MvPolynomial diff --git a/Mathlib/Algebra/MvPolynomial/Equiv.lean b/Mathlib/Algebra/MvPolynomial/Equiv.lean index 543505a067ec91..3a90eea82c3a26 100644 --- a/Mathlib/Algebra/MvPolynomial/Equiv.lean +++ b/Mathlib/Algebra/MvPolynomial/Equiv.lean @@ -502,7 +502,7 @@ theorem optionEquivLeft_elim_eval (s : S₁ → R) (y : R) (f : MvPolynomial (Op apply MvPolynomial.algHom_ext rw [Option.forall] simp only [aeval_X, Option.elim_none, AlgHom.coe_comp, Polynomial.coe_aeval_eq_eval, - AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_algHom, comp_apply, + AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_toAlgHom, comp_apply, optionEquivLeft_apply, Polynomial.map_X, Polynomial.eval_X, Option.elim_some, Polynomial.map_C, eval_X, Polynomial.eval_C, implies_true, and_self, φ] @@ -684,7 +684,7 @@ theorem eval_eq_eval_mv_eval' (s : Fin n → R) (y : R) (f : MvPolynomial (Fin ( apply MvPolynomial.algHom_ext rw [Fin.forall_iff_succ] simp only [aeval_X, Fin.cons_zero, AlgHom.coe_comp, Polynomial.coe_aeval_eq_eval, - AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_algHom, + AlgHom.coe_mk, Polynomial.coe_mapRingHom, AlgEquiv.coe_toAlgHom, comp_apply, finSuccEquiv_apply, eval₂Hom_X', Fin.cases_zero, Polynomial.map_X, Polynomial.eval_X, Fin.cons_succ, Fin.cases_succ, Polynomial.map_C, eval_X, Polynomial.eval_C, implies_true, and_self, φ] @@ -864,7 +864,7 @@ lemma Polynomial.toMvPolynomial_eq_rename_comp (i : σ) : lemma Polynomial.toMvPolynomial_injective (i : σ) : Function.Injective (toMvPolynomial (R := R) i) := by - simp only [toMvPolynomial_eq_rename_comp, AlgHom.coe_comp, AlgEquiv.coe_algHom, + simp only [toMvPolynomial_eq_rename_comp, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, EquivLike.injective_comp] exact MvPolynomial.rename_injective (fun x ↦ i) fun _ _ _ ↦ rfl diff --git a/Mathlib/Algebra/MvPolynomial/PDeriv.lean b/Mathlib/Algebra/MvPolynomial/PDeriv.lean index c70e5c5bb4f250..929abd0a853464 100644 --- a/Mathlib/Algebra/MvPolynomial/PDeriv.lean +++ b/Mathlib/Algebra/MvPolynomial/PDeriv.lean @@ -118,6 +118,21 @@ theorem pderiv_pow {i : σ} {f : MvPolynomial σ R} {n : ℕ} : theorem pderiv_C_mul {f : MvPolynomial σ R} {i : σ} : pderiv i (C a * f) = C a * pderiv i f := by rw [C_mul', Derivation.map_smul, C_mul'] +theorem coeff_pderiv {i : σ} (p : MvPolynomial σ R) (m : σ →₀ ℕ) : + coeff m (pderiv i p) = coeff (m + single i 1) p * (m i + 1) := by + classical + induction p using MvPolynomial.induction_on' with + | add p q hp hq => simp [hp, hq, add_mul] + | monomial n a => + rw [pderiv_monomial, coeff_monomial, coeff_monomial] + by_cases h : n = m + single i 1 + · simp [h] + simp only [h, ↓reduceIte, zero_mul] + by_cases hn : n i = 0 + · simp [hn] + apply if_neg + rwa [tsub_eq_iff_eq_add_of_le (fun _ ↦ by grind)] + theorem pderiv_map {S} [CommSemiring S] {φ : R →+* S} {f : MvPolynomial σ R} {i : σ} : pderiv i (map φ f) = map φ (pderiv i f) := by apply induction_on f (fun r ↦ by simp) (fun p q hp hq ↦ by simp [hp, hq]) fun p j eq ↦ ?_ diff --git a/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean b/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean index e857251935b284..fd6e51dbdf0f9f 100644 --- a/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean +++ b/Mathlib/Algebra/MvPolynomial/SchwartzZippel.lean @@ -197,7 +197,6 @@ lemma schwartz_zippel_totalDegree {n} {p : MvPolynomial (Fin n) R} (hp : p ≠ 0 _ = p.totalDegree / #S := by obtain rfl | hs := S.eq_empty_or_nonempty · simp - simp only [← _root_.bot_eq_zero, sup_bot] simp_rw [totalDegree, Nat.cast_finsetSup] rw [sup_div₀ (by positivity)] simp [← sum_div, Finsupp.sum_fintype] diff --git a/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean b/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean index 2438aca9fe70bd..7caaf3ed13c86c 100644 --- a/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean +++ b/Mathlib/Algebra/Order/AbsoluteValue/Basic.lean @@ -416,8 +416,8 @@ open Lean Meta Mathlib Meta Positivity Qq in For performance reasons, we only attempt to apply this when `abv` is a variable. If it is an explicit function, e.g. `|_|` or `‖_‖`, another extension should apply. -/ @[positivity _] -meta def Mathlib.Meta.Positivity.evalAbv : PositivityExt where eval {_ _α} _zα pα? e := do - let some _ := pα? | pure .none +meta def Mathlib.Meta.Positivity.evalAbv : PositivityExt where eval {_ _α} _zα pα? e := + match pα? with | none => pure .none | some _ => do let (.app f a) ← whnfR e | throwError "not abv ·" if !f.getAppFn.isFVar then throwError "abv: function is not a variable" diff --git a/Mathlib/Algebra/Order/AddTorsor.lean b/Mathlib/Algebra/Order/AddTorsor.lean index edd40c9ab230b6..18392fee332b1b 100644 --- a/Mathlib/Algebra/Order/AddTorsor.lean +++ b/Mathlib/Algebra/Order/AddTorsor.lean @@ -10,6 +10,7 @@ public import Mathlib.Algebra.Order.Monoid.Defs /-! # Ordered scalar multiplication and vector addition + This file defines ordered scalar multiplication and vector addition, and proves some properties. In the additive case, a motivating example is given by the additive action of `ℤ` on subsets of reals that are closed under integer translation. The order compatibility allows for a treatment of diff --git a/Mathlib/Algebra/Order/Algebra.lean b/Mathlib/Algebra/Order/Algebra.lean index c5a1cbd199b481..36fe510b349ab7 100644 --- a/Mathlib/Algebra/Order/Algebra.lean +++ b/Mathlib/Algebra/Order/Algebra.lean @@ -95,14 +95,13 @@ open Lean Meta Qq Function /-- Extension for `algebraMap`. -/ @[positivity algebraMap _ _ _] -meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ pβ? e := do +meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ pβ? e := + match pβ? with | none => pure .none | some _ => do let ~q(@algebraMap $α _ $instα $instβ $instαβ $a) := e | throwError "not `algebraMap`" - let pα ← try? <| synthInstanceQ q(PartialOrder $α) - match ← core q(inferInstance) pα a with + let some pα ← try? <| synthInstanceQ q(PartialOrder $α) | pure .none + match ← core q(inferInstance) (some pα) a with | .positive pa => - let some _ := pβ? | pure .none let _instαSemiring ← synthInstanceQ q(Semiring $α) - let _instαPartialOrder ← synthInstanceQ q(PartialOrder $α) try let _instβSemiring ← synthInstanceQ q(Semiring $β) let _instβPartialOrder ← synthInstanceQ q(PartialOrder $β) @@ -118,9 +117,7 @@ meta def evalAlgebraMap : PositivityExt where eval {u β} _zβ pβ? e := do assertInstancesCommute return .nonnegative q(algebraMap_nonneg $β <| le_of_lt $pa) | .nonnegative pa => - let some _ := pβ? | pure .none let _instαSemiring ← synthInstanceQ q(CommSemiring $α) - let _instαPartialOrder ← synthInstanceQ q(PartialOrder $α) let _instβSemiring ← synthInstanceQ q(Semiring $β) let _instβPartialOrder ← synthInstanceQ q(PartialOrder $β) let _instβIsOrderedRing ← synthInstanceQ q(IsOrderedRing $β) diff --git a/Mathlib/Algebra/Order/Antidiag/Pi.lean b/Mathlib/Algebra/Order/Antidiag/Pi.lean index b9f0282abd1dda..9202ca123bbffc 100644 --- a/Mathlib/Algebra/Order/Antidiag/Pi.lean +++ b/Mathlib/Algebra/Order/Antidiag/Pi.lean @@ -162,8 +162,8 @@ lemma pairwiseDisjoint_piAntidiag_map_addRightEmbedding (hi : i ∉ s) (n : μ) (antidiagonal n : Set (μ × μ)).PairwiseDisjoint fun p ↦ map (addRightEmbedding fun j ↦ if j = i then p.1 else 0) (s.piAntidiag p.2) := by rintro ⟨a, b⟩ hab ⟨c, d⟩ hcd - simp only [ne_eq, antidiagonal_congr' hab hcd, disjoint_left, mem_map, mem_piAntidiag, - addRightEmbedding_apply, not_exists, not_and, and_imp, forall_exists_index] + simp only [ne_eq, HasAntidiagonal.antidiagonal_congr' hab hcd, disjoint_left, mem_map, + mem_piAntidiag, addRightEmbedding_apply, not_exists, not_and, and_imp, forall_exists_index] rintro hfg _ f rfl - rfl g rfl - hgf exact hfg <| by simpa [sum_add_distrib, hi] using congr_arg (∑ j ∈ s, · j) hgf.symm diff --git a/Mathlib/Algebra/Order/Antidiag/Prod.lean b/Mathlib/Algebra/Order/Antidiag/Prod.lean index e53c0810966ba6..398a5813e4bc4f 100644 --- a/Mathlib/Algebra/Order/Antidiag/Prod.lean +++ b/Mathlib/Algebra/Order/Antidiag/Prod.lean @@ -5,6 +5,7 @@ Authors: Antoine Chambert-Loir, María Inés de Frutos-Fernández, Bhavik Mehta, -/ module +public import Mathlib.Algebra.Group.TypeTags.Basic public import Mathlib.Algebra.Order.Monoid.Canonical.Defs public import Mathlib.Algebra.Order.Sub.Defs public import Mathlib.Data.Finset.Basic @@ -16,6 +17,10 @@ We define a type class `Finset.HasAntidiagonal A` which contains a function `antidiagonal : A → Finset (A × A)` such that `antidiagonal n` is the finset of all pairs adding to `n`, as witnessed by `mem_antidiagonal`. +Analogously, the type class `Finset.HasMulAntidiagonal A` contains a function +`mulAntidiagonal : A → Finset (A × A)` such that `mulAntidiagonal n` +is the finset of all pairs multiplying to `n`, as witnessed by `mem_mulAntidiagonal`. + When `A` is a canonically ordered additive monoid with locally finite order this typeclass can be instantiated with `Finset.antidiagonalOfLocallyFinite`. This applies in particular when `A` is `ℕ`, more generally or `σ →₀ ℕ`, @@ -27,8 +32,9 @@ and any finiteness condition would be OK.) For computational reasons it is better to manually provide instances for `ℕ` and `σ →₀ ℕ`, to avoid quadratic runtime performance. -These instances are provided as `Finset.Nat.instHasAntidiagonal` and `Finsupp.instHasAntidiagonal`. -This is why `Finset.antidiagonalOfLocallyFinite` is an `abbrev` and not an `instance`. +These instances are provided as `Finset.Nat.instHasAntidiagonal` and +`Finsupp.instHasAntidiagonal`. +This is why `Finset.mulAntidiagonalOfLocallyFinite` is an `abbrev` and not an `instance`. This definition does not exactly match with that of `Multiset.antidiagonal` defined in `Mathlib/Data/Multiset/Antidiagonal.lean`, because of the multiplicities. @@ -45,8 +51,7 @@ def s : Multiset ℕ := {0, 0, 0} ## TODO -* Define `HasMulAntidiagonal` (for monoids). - For `PNat`, we will recover the set of divisors of a strictly positive integer. +* For `PNat`, `HasMulAntidiagonal` will recover the set of divisors of a strictly positive integer. -/ @[expose] public section @@ -55,9 +60,10 @@ open Function namespace Finset -/-- The class of additive monoids with an antidiagonal -/ +/-- The class of additive monoids with an antidiagonal. -/ class HasAntidiagonal (A : Type*) [AddMonoid A] where - /-- The antidiagonal of an element `n` is the finset of pairs `(i, j)` such that `i + j = n`. -/ + /-- The antidiagonal of an element `n` is the finset of pairs `(i, j)` such that + `i + j = n`. -/ antidiagonal : A → Finset (A × A) /-- A pair belongs to `antidiagonal n` iff the sum of its components is equal to `n`. -/ mem_antidiagonal {n} {a} : a ∈ antidiagonal n ↔ a.fst + a.snd = n @@ -66,88 +72,132 @@ export HasAntidiagonal (antidiagonal mem_antidiagonal) attribute [simp] mem_antidiagonal +/-- The class of (multiplicative) monoids with a mulAntidiagonal. -/ +class HasMulAntidiagonal (A : Type*) [Monoid A] where + /-- The mulAntidiagonal of an element `n` is the finset of pairs `(i, j)` such that + `i * j = n`. -/ + mulAntidiagonal : A → Finset (A × A) + /-- A pair belongs to `mulAntidiagonal n` iff the product of its components is equal to `n`. -/ + mem_mulAntidiagonal {n} {a} : a ∈ mulAntidiagonal n ↔ a.fst * a.snd = n + +attribute [to_additive] HasMulAntidiagonal + +export HasMulAntidiagonal (mulAntidiagonal mem_mulAntidiagonal) + +attribute [simp] HasMulAntidiagonal.mem_mulAntidiagonal + variable {A : Type*} -/-- All `HasAntidiagonal` instances are equal -/ -instance [AddMonoid A] : Subsingleton (HasAntidiagonal A) where +namespace HasMulAntidiagonal + +/-- All `HasMulAntidiagonal` instances are equal -/ +@[to_additive /-- All `HasAntidiagonal` instances are equal -/] +instance [Monoid A] : Subsingleton (HasMulAntidiagonal A) where allEq := by rintro ⟨a, ha⟩ ⟨b, hb⟩ congr with n xy rw [ha, hb] --- The goal of this lemma is to allow to rewrite antidiagonal +@[to_additive] +lemma nonempty_antidiagonal {M : Type*} [Monoid M] [Finset.HasMulAntidiagonal M] (a : M) : + (Finset.mulAntidiagonal a).Nonempty := + ⟨(1, a), by simp⟩ + +-- The goal of this lemma is to allow to rewrite mulAntidiagonal/antidiagonal -- when the decidability instances obfuscate Lean set_option linter.overlappingInstances false in -lemma hasAntidiagonal_congr (A : Type*) [AddMonoid A] - [H1 : HasAntidiagonal A] [H2 : HasAntidiagonal A] : - H1.antidiagonal = H2.antidiagonal := by congr!; subsingleton +@[to_additive] +lemma congr (A : Type*) [Monoid A] + [H1 : HasMulAntidiagonal A] [H2 : HasMulAntidiagonal A] : + H1.mulAntidiagonal = H2.mulAntidiagonal := by congr!; subsingleton -theorem swap_mem_antidiagonal [AddCommMonoid A] [HasAntidiagonal A] {n : A} {xy : A × A} : - xy.swap ∈ antidiagonal n ↔ xy ∈ antidiagonal n := by - simp [add_comm] +@[to_additive] +theorem swap_mem_mulAntidiagonal [CommMonoid A] [HasMulAntidiagonal A] {n : A} {xy : A × A} : + xy.swap ∈ mulAntidiagonal n ↔ xy ∈ mulAntidiagonal n := by + simp [mul_comm] -@[simp] theorem map_prodComm_antidiagonal [AddCommMonoid A] [HasAntidiagonal A] {n : A} : - (antidiagonal n).map (Equiv.prodComm A A) = antidiagonal n := - Finset.ext fun ⟨a, b⟩ => by simp [add_comm] +@[to_additive (attr := simp) map_prodComm_antidiagonal] +theorem map_prodComm_mulAntidiagonal [CommMonoid A] [HasMulAntidiagonal A] {n : A} : + (mulAntidiagonal n).map (Equiv.prodComm A A) = mulAntidiagonal n := + Finset.ext fun ⟨a, b⟩ => by simp [mul_comm] -/-- See also `Finset.map_prodComm_antidiagonal`. -/ -@[simp] theorem map_swap_antidiagonal [AddCommMonoid A] [HasAntidiagonal A] {n : A} : - (antidiagonal n).map ⟨Prod.swap, Prod.swap_injective⟩ = antidiagonal n := - map_prodComm_antidiagonal +/-- See also `Finset.map_prodComm_mulAntidiagonal`. -/ +@[to_additive (attr := simp)] +theorem map_swap_mulAntidiagonal [CommMonoid A] [HasMulAntidiagonal A] {n : A} : + (mulAntidiagonal n).map ⟨Prod.swap, Prod.swap_injective⟩ = mulAntidiagonal n := + map_prodComm_mulAntidiagonal -section AddCancelMonoid -variable [AddCancelMonoid A] [HasAntidiagonal A] {p q : A × A} {n : A} +section CancelMonoid +variable [CancelMonoid A] [HasMulAntidiagonal A] {p q : A × A} {n : A} + +/-- A point in the mulAntidiagonal is determined by its first coordinate. + +See also `Finset.mulAntidiagonal_congr'`. -/ +@[to_additive /-- A point in the antidiagonal is determined by its first coordinate. -See also `Finset.antidiagonal_congr'`. -/ -theorem antidiagonal_congr (hp : p ∈ antidiagonal n) (hq : q ∈ antidiagonal n) : +See also `Finset.antidiagonal_congr'`. -/] +theorem mulAntidiagonal_congr (hp : p ∈ mulAntidiagonal n) (hq : q ∈ mulAntidiagonal n) : p = q ↔ p.1 = q.1 := by - refine ⟨congr_arg Prod.fst, fun h ↦ Prod.ext h ((add_right_inj q.fst).mp ?_)⟩ - rw [mem_antidiagonal] at hp hq + refine ⟨congr_arg Prod.fst, fun h ↦ Prod.ext h ((mul_right_inj q.fst).mp ?_)⟩ + rw [mem_mulAntidiagonal] at hp hq rw [hq, ← h, hp] +/-- A point in the mulAntidiagonal is determined by its first co-ordinate (subtype version of +`Finset.mulAntidiagonal_congr`). This lemma is used by the `ext` tactic. -/ +@[to_additive (attr := ext) /-- A point in the antidiagonal is determined by its first co-ordinate (subtype version of -`Finset.antidiagonal_congr`). This lemma is used by the `ext` tactic. -/ -@[ext] theorem antidiagonal_subtype_ext {p q : antidiagonal n} (h : p.val.1 = q.val.1) : p = q := - Subtype.ext ((antidiagonal_congr p.prop q.prop).mpr h) +`Finset.antidiagonal_congr`). This lemma is used by the `ext` tactic. -/] +theorem mulAntidiagonal_subtype_ext {p q : mulAntidiagonal n} (h : p.val.1 = q.val.1) : p = q := + Subtype.ext ((mulAntidiagonal_congr p.prop q.prop).mpr h) + +end CancelMonoid -end AddCancelMonoid +section CancelCommMonoid +variable [CancelCommMonoid A] [HasMulAntidiagonal A] {p q : A × A} {n : A} -section AddCancelCommMonoid -variable [AddCancelCommMonoid A] [HasAntidiagonal A] {p q : A × A} {n : A} +/-- A point in the mulAntidiagonal is determined by its second coordinate. -/-- A point in the antidiagonal is determined by its second coordinate. +See also `Finset.mulAntidiagonal_congr`. -/ +@[to_additive /-- A point in the antidiagonal is determined by its second coordinate. -See also `Finset.antidiagonal_congr`. -/ -lemma antidiagonal_congr' (hp : p ∈ antidiagonal n) (hq : q ∈ antidiagonal n) : +See also `Finset.antidiagonal_congr`. -/] +lemma mulAntidiagonal_congr' (hp : p ∈ mulAntidiagonal n) (hq : q ∈ mulAntidiagonal n) : p = q ↔ p.2 = q.2 := by rw [← Prod.swap_inj] - exact antidiagonal_congr (swap_mem_antidiagonal.2 hp) (swap_mem_antidiagonal.2 hq) + exact mulAntidiagonal_congr (swap_mem_mulAntidiagonal.2 hp) (swap_mem_mulAntidiagonal.2 hq) -end AddCancelCommMonoid +end CancelCommMonoid -section CanonicallyOrderedAdd -variable [AddCommMonoid A] [PartialOrder A] [CanonicallyOrderedAdd A] [HasAntidiagonal A] +section CanonicallyOrderedMul -@[simp] -theorem antidiagonal_zero : antidiagonal (0 : A) = {(0, 0)} := by +variable [CommMonoid A] [PartialOrder A] [CanonicallyOrderedMul A] [HasMulAntidiagonal A] + +@[to_additive (attr := simp)] +theorem mulAntidiagonal_one : mulAntidiagonal (1 : A) = {(1, 1)} := by ext ⟨x, y⟩ simp -theorem antidiagonal.fst_le {n : A} {kl : A × A} (hlk : kl ∈ antidiagonal n) : kl.1 ≤ n := by - rw [le_iff_exists_add] +@[to_additive] +theorem mulAntidiagonal.fst_le {n : A} {kl : A × A} (hlk : kl ∈ mulAntidiagonal n) : kl.1 ≤ n := by + rw [le_iff_exists_mul] use kl.2 - rwa [mem_antidiagonal, eq_comm] at hlk + rwa [mem_mulAntidiagonal, eq_comm] at hlk -theorem antidiagonal.snd_le {n : A} {kl : A × A} (hlk : kl ∈ antidiagonal n) : kl.2 ≤ n := by - rw [le_iff_exists_add] +@[to_additive] +theorem mulAntidiagonal.snd_le {n : A} {kl : A × A} (hlk : kl ∈ mulAntidiagonal n) : kl.2 ≤ n := by + rw [le_iff_exists_mul] use kl.1 - rwa [mem_antidiagonal, eq_comm, add_comm] at hlk + rwa [mem_mulAntidiagonal, eq_comm, mul_comm] at hlk + +end CanonicallyOrderedMul -end CanonicallyOrderedAdd +end HasMulAntidiagonal +namespace HasAntidiagonal section OrderedSub + variable [AddCommMonoid A] [PartialOrder A] [CanonicallyOrderedAdd A] [Sub A] [OrderedSub A] variable [AddLeftReflectLE A] variable [HasAntidiagonal A] @@ -173,30 +223,66 @@ theorem filter_snd_eq_antidiagonal (n m : A) [DecidablePred (· = m)] [Decidable end OrderedSub -/-- The disjoint union of antidiagonals `Σ (n : A), antidiagonal n` is equivalent to the product - `A × A`. This is such an equivalence, obtained by mapping `(n, (k, l))` to `(k, l)`. -/ -@[simps] -def sigmaAntidiagonalEquivProd [AddMonoid A] [HasAntidiagonal A] : - (Σ n : A, antidiagonal n) ≃ A × A where +end HasAntidiagonal + +namespace HasMulAntidiagonal + +/-- The disjoint union of mulAntidiagonals `Σ (n : A), mulAntidiagonal n` is equivalent to the + product `A × A`. This is such an equivalence, obtained by mapping `(n, (k, l))` to `(k, l)`. -/ +@[to_additive (attr := simps) sigmaAntidiagonalEquivProd +/-- The disjoint union of antidiagonals `Σ (n : A), antidiagonal n` is equivalent to the + product `A × A`. This is such an equivalence, obtained by mapping `(n, (k, l))` to `(k, l)`. -/] +def sigmaMulAntidiagonalEquivProd [Monoid A] [HasMulAntidiagonal A] : + (Σ n : A, mulAntidiagonal n) ≃ A × A where toFun x := x.2 - invFun x := ⟨x.1 + x.2, x, mem_antidiagonal.mpr rfl⟩ + invFun x := ⟨x.1 * x.2, x, mem_mulAntidiagonal.mpr rfl⟩ left_inv := by rintro ⟨n, ⟨k, l⟩, h⟩ - rw [mem_antidiagonal] at h + rw [mem_mulAntidiagonal] at h exact Sigma.subtype_ext h rfl +section + variable {A : Type*} - [AddCommMonoid A] [PartialOrder A] [CanonicallyOrderedAdd A] + [CommMonoid A] [PartialOrder A] [CanonicallyOrderedMul A] [LocallyFiniteOrderBot A] [DecidableEq A] +/-- In a canonically ordered multiplicative monoid, the mulAntidiagonal can be constructed by +filtering. + +Note that this is not an instance, as for sometimes a more efficient algorithm is available. -/ +@[to_additive /-- In a canonically ordered additive monoid, the antidiagonal can be construct by filtering. -Note that this is not an instance, as for some times a more efficient algorithm is available. -/ -abbrev antidiagonalOfLocallyFinite : HasAntidiagonal A where - antidiagonal n := {uv ∈ Iic n ×ˢ Iic n | uv.fst + uv.snd = n} - mem_antidiagonal {n} {a} := by +Note that this is not an instance, as for some times a more efficient algorithm is available. -/] +abbrev mulAntidiagonalOfLocallyFinite : HasMulAntidiagonal A where + mulAntidiagonal n := {uv ∈ Iic n ×ˢ Iic n | uv.fst * uv.snd = n} + mem_mulAntidiagonal {n} {a} := by simp only [mem_filter, and_iff_right_iff_imp] intro h simp [← h] +end + +section Multiplicative + +open Multiplicative + +variable {A : Type*} [AddMonoid A] [HasAntidiagonal A] + +instance : HasMulAntidiagonal (Multiplicative A) where + mulAntidiagonal a := + (antidiagonal (toAdd a)).map ⟨fun p ↦ (ofAdd p.1 , ofAdd p.2), fun _ _ h ↦ by aesop⟩ + mem_mulAntidiagonal {a p} := by aesop + +lemma mem_mulAntidiagonal_ofAdd_iff_toAdd_mem_antidiagonal {a : A} + {p : Multiplicative A × Multiplicative A} : + p ∈ mulAntidiagonal (ofAdd a) ↔ (toAdd p.1, toAdd p.2) ∈ antidiagonal a := by + simp only [mem_mulAntidiagonal, mem_antidiagonal] + rw [Multiplicative.ext_iff, toAdd_mul, toAdd_ofAdd] + +end Multiplicative + +end HasMulAntidiagonal + end Finset diff --git a/Mathlib/Algebra/Order/Antidiag/Tendsto.lean b/Mathlib/Algebra/Order/Antidiag/Tendsto.lean new file mode 100644 index 00000000000000..34a9de60c58691 --- /dev/null +++ b/Mathlib/Algebra/Order/Antidiag/Tendsto.lean @@ -0,0 +1,39 @@ +/- +Copyright (c) 2026 William Coram. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: William Coram +-/ +module + +public import Mathlib.Algebra.Group.Pointwise.Set.Finite +public import Mathlib.Algebra.Order.Antidiag.Prod +public import Mathlib.Order.Filter.Cofinite + +/-! +# Antidiagonal tendsto + +`tendsto_sup'_antidiagonal_cofinite`: If a function `f : M × M → R` on a Finset `M`, that has the + antidiagonal propertry, tends to to a filter `F` under the cofinite filter then so does the + function assigning to `x : M` its supremum of its antidiagonal. +-/ + +@[expose] public section + +namespace Finset.HasAntidiagonal + +open Filter + +variable {M R : Type*} [AddMonoid M] [HasAntidiagonal M] {f : M × M → R} [LinearOrder R] + {F : Filter R} + +lemma tendsto_sup'_antidiagonal_cofinite (hf : Tendsto f cofinite F) : Tendsto + (fun a ↦ (Finset.antidiagonal a).sup' (nonempty_antidiagonal _) f) cofinite F := by + intro U hU + refine ((((hf hU).image Prod.fst)).add ((hf hU).image Prod.snd)).subset ?_ + simp only [Set.subset_def, Set.mem_compl_iff, Set.mem_preimage] + intro x hx + obtain ⟨i, hi, e⟩ := Finset.exists_mem_eq_sup' (nonempty_antidiagonal x) f + obtain rfl : i.1 + i.2 = x := by simpa using hi + exact Set.add_mem_add (by simpa using ⟨i.2, e ▸ hx⟩) (by simpa using ⟨i.1, e ▸ hx⟩) + +end Finset.HasAntidiagonal diff --git a/Mathlib/Algebra/Order/Archimedean/IndicatorCard.lean b/Mathlib/Algebra/Order/Archimedean/IndicatorCard.lean index c4158ac10aa9e8..61be60c432d313 100644 --- a/Mathlib/Algebra/Order/Archimedean/IndicatorCard.lean +++ b/Mathlib/Algebra/Order/Archimedean/IndicatorCard.lean @@ -14,6 +14,7 @@ public import Mathlib.SetTheory.Cardinal.Finite /-! # Cardinality and limit of sum of indicators + This file contains results relating the cardinality of subsets of ℕ and limits, limsups of sums of indicators. diff --git a/Mathlib/Algebra/Order/BigOperators/Expect.lean b/Mathlib/Algebra/Order/BigOperators/Expect.lean index 2f7e1bf8a89631..5b72ba801fa25a 100644 --- a/Mathlib/Algebra/Order/BigOperators/Expect.lean +++ b/Mathlib/Algebra/Order/BigOperators/Expect.lean @@ -220,22 +220,22 @@ open scoped BigOperators attribute [local instance] monadLiftOptionMetaM in /-- Positivity extension for `Finset.expect`. -/ @[positivity Finset.expect _ _] -meta def evalFinsetExpect : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalFinsetExpect : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match e with | ~q(@Finset.expect $ι _ $instα $instmod $s $f) => let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque have body : Q($α) := .betaRev f #[i] let rbody ← core zα pα body - let p_pos : Option Q(0 < $e) ← (do + let p_pos : Option Q(0 < $e) ← do let .positive pbody := rbody | pure none -- Fail if the body is not provably positive let some ps ← proveFinsetNonempty s | pure none let .some pα' ← trySynthInstanceQ q(IsOrderedCancelAddMonoid $α) | pure none let .some instαordsmul ← trySynthInstanceQ q(PosSMulStrictMono ℚ≥0 $α) | pure none assumeInstancesCommute let pr : Q(∀ i, 0 < $f i) ← mkLambdaFVars #[i] pbody - return some - q(@expect_pos $ι $α $instα $pα $pα' $instmod $instαordsmul $s $f (fun i _ ↦ $pr i) $ps)) + pure <| some + q(@expect_pos $ι $α $instα $pα $pα' $instmod $instαordsmul $s $f (fun i _ ↦ $pr i) $ps) -- Try to show that the sum is positive if let some p_pos := p_pos then return .positive p_pos diff --git a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean index f4b2836ef9c166..0d96d6a018402a 100644 --- a/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean +++ b/Mathlib/Algebra/Order/BigOperators/Ring/Finset.lean @@ -225,16 +225,16 @@ example (s : Finset ℕ) (f : ℕ → ℤ) (hf : ∀ n, 0 ≤ f n) : 0 ≤ s.pro because `compareHyp` can't look for assumptions behind binders. -/ @[positivity Finset.prod _ _] -meta def evalFinsetProd : PositivityExt where eval {u α} zα pα? e := do +meta def evalFinsetProd : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match e with | ~q(@Finset.prod $ι _ $instα $s $f) => - let some pα := pα? | pure .none let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque have body : Q($α) := Expr.betaRev f #[i] let rbody ← core zα pα body let _instαmon ← synthInstanceQ q(CommMonoidWithZero $α) -- Try to show that the product is positive - let p_pos : Option Q(0 < $e) := ← do + let p_pos : Option Q(0 < $e) ← do let .positive pbody := rbody | pure none -- Fail if the body is not provably positive -- TODO(https://github.com/leanprover-community/quote4/issues/38): -- We must name the following, else `assertInstancesCommute` loops. @@ -243,19 +243,19 @@ meta def evalFinsetProd : PositivityExt where eval {u α} zα pα? e := do let .some _instαnontriv ← trySynthInstanceQ q(Nontrivial $α) | pure none assertInstancesCommute let pr : Q(∀ i, 0 < $f i) ← mkLambdaFVars #[i] pbody (binderInfoForMVars := .default) - return some q(prod_pos fun i _ ↦ $pr i) + pure <| some q(prod_pos fun i _ ↦ $pr i) if let some p_pos := p_pos then return .positive p_pos -- Try to show that the product is nonnegative - let p_nonneg : Option Q(0 ≤ $e) := ← do + let p_nonneg : Option Q(0 ≤ $e) ← do let some pbody := rbody.toNonneg - | return none -- Fail if the body is not provably nonnegative + | pure none -- Fail if the body is not provably nonnegative let pr : Q(∀ i, 0 ≤ $f i) ← mkLambdaFVars #[i] pbody (binderInfoForMVars := .default) -- TODO(https://github.com/leanprover-community/quote4/issues/38): -- We must name the following, else `assertInstancesCommute` loops. let .some _instαzeroone ← trySynthInstanceQ q(ZeroLEOneClass $α) | pure none let .some _instαposmul ← trySynthInstanceQ q(PosMulMono $α) | pure none assertInstancesCommute - return some q(prod_nonneg fun i _ ↦ $pr i) + pure <| some q(prod_nonneg fun i _ ↦ $pr i) if let some p_nonneg := p_nonneg then return .nonnegative p_nonneg -- Fall back to showing that the product is nonzero let pbody ← rbody.toNonzero diff --git a/Mathlib/Algebra/Order/CauSeq/Basic.lean b/Mathlib/Algebra/Order/CauSeq/Basic.lean index c3f8165c29f6db..21c801e49935a3 100644 --- a/Mathlib/Algebra/Order/CauSeq/Basic.lean +++ b/Mathlib/Algebra/Order/CauSeq/Basic.lean @@ -50,8 +50,7 @@ theorem rat_add_continuous_lemma {ε : α} (ε0 : 0 < ε) : ∃ δ > 0, ∀ {a₁ a₂ b₁ b₂ : β}, abv (a₁ - b₁) < δ → abv (a₂ - b₂) < δ → abv (a₁ + a₂ - (b₁ + b₂)) < ε := ⟨ε / 2, half_pos ε0, fun {a₁ a₂ b₁ b₂} h₁ h₂ => by - simpa [add_halves, sub_eq_add_neg, add_comm, add_left_comm, add_assoc] using - lt_of_le_of_lt (abv_add abv _ _) (add_lt_add h₁ h₂)⟩ + grw [add_sub_add_comm, abv_add abv, h₁, h₂, add_halves]⟩ theorem rat_mul_continuous_lemma {ε K₁ K₂ : α} (ε0 : 0 < ε) : ∃ δ > 0, ∀ {a₁ a₂ b₁ b₂ : β}, abv a₁ < K₁ → abv b₂ < K₂ → abv (a₁ - b₁) < δ → @@ -62,11 +61,10 @@ theorem rat_mul_continuous_lemma {ε K₁ K₂ : α} (ε0 : 0 < ε) : replace ha₁ := lt_of_lt_of_le ha₁ (le_trans (le_max_left _ K₂) (le_max_right 1 _)) replace hb₂ := lt_of_lt_of_le hb₂ (le_trans (le_max_right K₁ _) (le_max_right 1 _)) set M := max 1 (max K₁ K₂) - have : abv (a₁ - b₁) * abv b₂ + abv (a₂ - b₂) * abv a₁ < ε / 2 / M * M + ε / 2 / M * M := by - gcongr - rw [← abv_mul abv, mul_comm, div_mul_cancel₀ _ (ne_of_gt K0), ← abv_mul abv, add_halves] at this - simpa [sub_eq_add_neg, mul_add, add_mul, add_left_comm] using - lt_of_le_of_lt (abv_add abv _ _) this + suffices abv ((a₁ - b₁) * b₂ + a₁ * (a₂ - b₂)) < ε by + simpa [sub_eq_add_neg, mul_add, add_mul, add_left_comm] using this + grw [abv_add abv, abv_mul abv, abv_mul abv, h₁.le, h₂.le, ha₁, hb₂, mul_comm M, + div_mul_cancel₀ _ (ne_of_gt K0), add_halves] theorem rat_inv_continuous_lemma {β : Type*} [DivisionRing β] (abv : β → α) [IsAbsoluteValue abv] {ε K : α} (ε0 : 0 < ε) (K0 : 0 < K) : @@ -76,11 +74,8 @@ theorem rat_inv_continuous_lemma {β : Type*} [DivisionRing β] (abv : β → α have b0 := K0.trans_le hb rw [inv_sub_inv' ((abv_pos abv).1 a0) ((abv_pos abv).1 b0), abv_mul abv, abv_mul abv, abv_inv abv, abv_inv abv, abv_sub abv] - refine lt_of_mul_lt_mul_left (lt_of_mul_lt_mul_right ?_ b0.le) a0.le - rw [mul_assoc, inv_mul_cancel_right₀ b0.ne', ← mul_assoc, mul_inv_cancel₀ a0.ne', one_mul] - refine h.trans_le ?_ - gcongr - exact mul_nonneg a0.le ε0.le + grw [← ha, mul_assoc, ← hb, h] + simp [K0.ne'] end diff --git a/Mathlib/Algebra/Order/Field/Basic.lean b/Mathlib/Algebra/Order/Field/Basic.lean index ca4d841b0657e8..f50b2c06f3020b 100644 --- a/Mathlib/Algebra/Order/Field/Basic.lean +++ b/Mathlib/Algebra/Order/Field/Basic.lean @@ -740,26 +740,28 @@ such that `positivity` successfully recognises both `a` and `b`. -/ trace[Tactic.positivity.zeroness] "evalDiv: {a} divided by {b}" let _a ← synthInstanceQ q(Semifield $α) let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HDiv.hDiv) - let some pα := pα? | + match (dependent := true) pα? with + | none => match ← core zα pα? a, ← core zα pα? b with | .nonzero pa, .nonzero pb => let _a ← synthInstanceQ q(GroupWithZero $α) assumeInstancesCommute pure (.nonzero q(div_ne_zero $pa $pb)) | _, _ => pure .none - let _a ← synthInstanceQ q(GroupWithZero $α) - let _a ← synthInstanceQ q(PosMulReflectLT $α) - assumeInstancesCommute - let ra ← core zα pα a; let rb ← core zα pα b - match ra, rb with - | .positive pa, .positive pb => pure (.positive q(div_pos $pa $pb)) - | .positive pa, .nonnegative pb => pure (.nonnegative q(div_nonneg_of_pos_of_nonneg $pa $pb)) - | .nonnegative pa, .positive pb => pure (.nonnegative q(div_nonneg_of_nonneg_of_pos $pa $pb)) - | .nonnegative pa, .nonnegative pb => pure (.nonnegative q(div_nonneg $pa $pb)) - | .positive pa, .nonzero pb => pure (.nonzero q(div_ne_zero_of_pos_of_ne_zero $pa $pb)) - | .nonzero pa, .positive pb => pure (.nonzero q(div_ne_zero_of_ne_zero_of_pos $pa $pb)) - | .nonzero pa, .nonzero pb => pure (.nonzero q(div_ne_zero $pa $pb)) - | _, _ => pure .none + | some pα => + let _a ← synthInstanceQ q(GroupWithZero $α) + let _a ← synthInstanceQ q(PosMulReflectLT $α) + assumeInstancesCommute + let ra ← core zα pα a; let rb ← core zα pα b + match ra, rb with + | .positive pa, .positive pb => pure (.positive q(div_pos $pa $pb)) + | .positive pa, .nonnegative pb => pure (.nonnegative q(div_nonneg_of_pos_of_nonneg $pa $pb)) + | .nonnegative pa, .positive pb => pure (.nonnegative q(div_nonneg_of_nonneg_of_pos $pa $pb)) + | .nonnegative pa, .nonnegative pb => pure (.nonnegative q(div_nonneg $pa $pb)) + | .positive pa, .nonzero pb => pure (.nonzero q(div_ne_zero_of_pos_of_ne_zero $pa $pb)) + | .nonzero pa, .positive pb => pure (.nonzero q(div_ne_zero_of_ne_zero_of_pos $pa $pb)) + | .nonzero pa, .nonzero pb => pure (.nonzero q(div_ne_zero $pa $pb)) + | _, _ => pure .none /-- The `positivity` extension which identifies expressions of the form `a⁻¹`, such that `positivity` successfully recognises `a`. -/ @@ -769,33 +771,35 @@ meta def evalInv : PositivityExt where eval {u α} zα pα? e := do let _e_eq : $e =Q $f $a := ⟨⟩ let _a ← synthInstanceQ q(Semifield $α) let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(Inv.inv) - let some _ := pα? | + match (dependent := true) pα? with + | none => match ← core zα pα? a with | .nonzero pa => let _a ← synthInstanceQ q(GroupWithZero $α) assumeInstancesCommute pure (.nonzero q(inv_ne_zero $pa)) | _ => pure .none - let _a ← synthInstanceQ q(GroupWithZero $α) - let _a ← synthInstanceQ q(PartialOrder $α) - let _a ← synthInstanceQ q(PosMulReflectLT $α) - assumeInstancesCommute - let ra ← core zα pα? a - match ra with - | .positive pa => + | some pα => + let _a ← synthInstanceQ q(GroupWithZero $α) + let _a ← synthInstanceQ q(PartialOrder $α) + let _a ← synthInstanceQ q(PosMulReflectLT $α) assumeInstancesCommute - pure (.positive q(inv_pos_of_pos $pa)) - | .nonnegative pa => - assumeInstancesCommute - pure (.nonnegative q(inv_nonneg_of_nonneg $pa)) - | .nonzero pa => pure (.nonzero q(inv_ne_zero $pa)) - | .none => pure .none + let ra ← core zα (some pα) a + match ra with + | .positive pa => + assumeInstancesCommute + pure (.positive q(inv_pos_of_pos $pa)) + | .nonnegative pa => + assumeInstancesCommute + pure (.nonnegative q(inv_nonneg_of_nonneg $pa)) + | .nonzero pa => pure (.nonzero q(inv_ne_zero $pa)) + | .none => pure .none /-- The `positivity` extension which identifies expressions of the form `a ^ (0:ℤ)`. -/ @[positivity _ ^ (0 : ℤ), Pow.pow _ (0 : ℤ)] -meta def evalPowZeroInt : PositivityExt where eval {u α} _zα pα? e := do +meta def evalPowZeroInt : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do let .app (.app _ (a : Q($α))) _ ← withReducible (whnf e) | throwError "not ^" - let some _ := pα? | pure .none let _a ← synthInstanceQ q(Semifield $α) let _a ← synthInstanceQ q(LinearOrder $α) let _a ← synthInstanceQ q(IsStrictOrderedRing $α) diff --git a/Mathlib/Algebra/Order/Field/Power.lean b/Mathlib/Algebra/Order/Field/Power.lean index 921d4a96f7f17f..99b8427345bff4 100644 --- a/Mathlib/Algebra/Order/Field/Power.lean +++ b/Mathlib/Algebra/Order/Field/Power.lean @@ -125,7 +125,8 @@ such that `positivity` successfully recognises both `a` and `b`. -/ @[positivity _ ^ (_ : ℤ), Pow.pow _ (_ : ℤ)] meta def evalZPow : PositivityExt where eval {u α} zα pα? e := do let .app (.app _ (a : Q($α))) (b : Q(ℤ)) ← withReducible (whnf e) | throwError "not ^" - let some pα := pα? | + match (dependent := true) pα? with + | none => match ← core zα pα? a with | .nonzero pa => let _a ← synthInstanceQ q(GroupWithZero $α) @@ -133,61 +134,62 @@ meta def evalZPow : PositivityExt where eval {u α} zα pα? e := do haveI' : $e =Q $a ^ $b := ⟨⟩ pure (.nonzero q(zpow_ne_zero $b $pa)) | _ => pure .none - let result ← catchNone do - let _a ← synthInstanceQ q(Field $α) - let _a ← synthInstanceQ q(LinearOrder $α) - let _a ← synthInstanceQ q(IsStrictOrderedRing $α) - assumeInstancesCommute - match ← whnfR b with - | .app (.app (.app (.const `OfNat.ofNat _) _) (.lit (Literal.natVal n))) _ => - guard (n % 2 = 0) - have m : Q(ℕ) := mkRawNatLit (n / 2) - haveI' : $b =Q $m + $m := ⟨⟩ - haveI' : $e =Q $a ^ $b := ⟨⟩ - pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a)) - | .app (.app (.app (.const `Neg.neg _) _) _) b' => - let b' ← whnfR b' - let .true := b'.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ -n where n is a literal" - let some n := (b'.getRevArg! 1).rawNatLit? | throwError "not a ^ -n where n is a literal" - guard (n % 2 = 0) - have m : Q(ℕ) := mkRawNatLit (n / 2) - haveI' : $b =Q (-$m) + (-$m) := ⟨⟩ - haveI' : $e =Q $a ^ $b := ⟨⟩ - pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a)) - | _ => throwError "not a ^ n where n is a literal or a negated literal" - orElse result do - let ra ← core zα pα a - let ofNonneg (pa : Q(0 ≤ $a)) - (_oα : Q(Semifield $α)) (_oα : Q(LinearOrder $α)) (_oα : Q(IsStrictOrderedRing $α)) : - MetaM (Strictness zα e pα) := do - haveI' : $e =Q $a ^ $b := ⟨⟩ - assumeInstancesCommute - pure (.nonnegative q(zpow_nonneg $pa $b)) - let ofNonzero (pa : Q($a ≠ 0)) (_oα : Q(GroupWithZero $α)) : MetaM (Strictness zα e pα) := do - haveI' : $e =Q $a ^ $b := ⟨⟩ - let _a ← synthInstanceQ q(GroupWithZero $α) + | some pα => + let result ← catchNone do + let _a ← synthInstanceQ q(Field $α) + let _a ← synthInstanceQ q(LinearOrder $α) + let _a ← synthInstanceQ q(IsStrictOrderedRing $α) assumeInstancesCommute - pure (.nonzero q(zpow_ne_zero $b $pa)) - match ra with - | .positive pa => - try - let _a ← synthInstanceQ q(Semifield $α) - let _a ← synthInstanceQ q(LinearOrder $α) - let _a ← synthInstanceQ q(IsStrictOrderedRing $α) + match ← whnfR b with + | .app (.app (.app (.const `OfNat.ofNat _) _) (.lit (Literal.natVal n))) _ => + guard (n % 2 = 0) + have m : Q(ℕ) := mkRawNatLit (n / 2) + haveI' : $b =Q $m + $m := ⟨⟩ + haveI' : $e =Q $a ^ $b := ⟨⟩ + pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a)) + | .app (.app (.app (.const `Neg.neg _) _) _) b' => + let b' ← whnfR b' + let .true := b'.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ -n where n is a literal" + let some n := (b'.getRevArg! 1).rawNatLit? | throwError "not a ^ -n where n is a literal" + guard (n % 2 = 0) + have m : Q(ℕ) := mkRawNatLit (n / 2) + haveI' : $b =Q (-$m) + (-$m) := ⟨⟩ + haveI' : $e =Q $a ^ $b := ⟨⟩ + pure (.nonnegative q(Even.zpow_nonneg (Even.add_self _) $a)) + | _ => throwError "not a ^ n where n is a literal or a negated literal" + orElse result do + let ra ← core zα pα a + let ofNonneg (pa : Q(0 ≤ $a)) + (_oα : Q(Semifield $α)) (_oα : Q(LinearOrder $α)) (_oα : Q(IsStrictOrderedRing $α)) : + MetaM (Strictness zα e pα) := do + haveI' : $e =Q $a ^ $b := ⟨⟩ assumeInstancesCommute + pure (.nonnegative q(zpow_nonneg $pa $b)) + let ofNonzero (pa : Q($a ≠ 0)) (_oα : Q(GroupWithZero $α)) : MetaM (Strictness zα e pα) := do haveI' : $e =Q $a ^ $b := ⟨⟩ - pure (.positive q(zpow_pos $pa $b)) - catch e : Exception => - trace[Tactic.positivity.failure] "{e.toMessageData}" - let sα ← synthInstanceQ q(Semifield $α) - let oα ← synthInstanceQ q(LinearOrder $α) - let iα ← synthInstanceQ q(IsStrictOrderedRing $α) - orElse (← catchNone (ofNonneg q(le_of_lt $pa) sα oα iα)) - (ofNonzero q(ne_of_gt $pa) q(inferInstance)) - | .nonnegative pa => - ofNonneg pa (← synthInstanceQ (_ : Q(Type u))) - (← synthInstanceQ (_ : Q(Type u))) (← synthInstanceQ (_ : Q(Prop))) - | .nonzero pa => ofNonzero pa (← synthInstanceQ (_ : Q(Type u))) - | .none => pure .none + let _a ← synthInstanceQ q(GroupWithZero $α) + assumeInstancesCommute + pure (.nonzero q(zpow_ne_zero $b $pa)) + match ra with + | .positive pa => + try + let _a ← synthInstanceQ q(Semifield $α) + let _a ← synthInstanceQ q(LinearOrder $α) + let _a ← synthInstanceQ q(IsStrictOrderedRing $α) + assumeInstancesCommute + haveI' : $e =Q $a ^ $b := ⟨⟩ + pure (.positive q(zpow_pos $pa $b)) + catch e : Exception => + trace[Tactic.positivity.failure] "{e.toMessageData}" + let sα ← synthInstanceQ q(Semifield $α) + let oα ← synthInstanceQ q(LinearOrder $α) + let iα ← synthInstanceQ q(IsStrictOrderedRing $α) + orElse (← catchNone (ofNonneg q(le_of_lt $pa) sα oα iα)) + (ofNonzero q(ne_of_gt $pa) q(inferInstance)) + | .nonnegative pa => + ofNonneg pa (← synthInstanceQ (_ : Q(Type u))) + (← synthInstanceQ (_ : Q(Type u))) (← synthInstanceQ (_ : Q(Prop))) + | .nonzero pa => ofNonzero pa (← synthInstanceQ (_ : Q(Type u))) + | .none => pure .none end Mathlib.Meta.Positivity diff --git a/Mathlib/Algebra/Order/Floor/Extended.lean b/Mathlib/Algebra/Order/Floor/Extended.lean index 3708cc1a32b067..3b73682846a96d 100644 --- a/Mathlib/Algebra/Order/Floor/Extended.lean +++ b/Mathlib/Algebra/Order/Floor/Extended.lean @@ -256,10 +256,10 @@ alias ⟨_, natCeil_pos⟩ := ENat.ceil_pos /-- Extension for the `positivity` tactic: `ENat.ceil` is positive if its input is. -/ @[positivity ⌈_⌉ₑ] -meta def evalENatCeil : PositivityExt where eval {u α} _zα pα? e := do +meta def evalENatCeil : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ∞), ~q(ENat.ceil $r) => - let some _ := pα? | pure .none match ← core q(inferInstance) (some q(inferInstance)) r with | .positive pr => assertInstancesCommute diff --git a/Mathlib/Algebra/Order/Floor/Ring.lean b/Mathlib/Algebra/Order/Floor/Ring.lean index a7b86a5d802241..8aeced5d83d30d 100644 --- a/Mathlib/Algebra/Order/Floor/Ring.lean +++ b/Mathlib/Algebra/Order/Floor/Ring.lean @@ -50,10 +50,10 @@ theorem int_floor_nonneg_of_pos [Ring α] [LinearOrder α] [FloorRing α] {a : /-- Extension for the `positivity` tactic: `Int.floor` is nonnegative if its input is. -/ @[positivity ⌊_⌋] -meta def evalIntFloor : PositivityExt where eval {u α} _zα pα? e := do +meta def evalIntFloor : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℤ), ~q(@Int.floor $α' $ir $io $j $a) => - let some _ := pα? | pure .none match ← core q(inferInstance) (some q(inferInstance)) a with | .positive pa => assertInstancesCommute @@ -70,10 +70,10 @@ theorem nat_ceil_pos [Semiring α] [LinearOrder α] [FloorSemiring α] {a : α} /-- Extension for the `positivity` tactic: `Nat.ceil` is positive if its input is. -/ @[positivity ⌈_⌉₊] -meta def evalNatCeil : PositivityExt where eval {u α} _zα pα? e := do +meta def evalNatCeil : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(@Nat.ceil $α' $ir $io $j $a) => - let some _ := pα? | pure .none let _i ← synthInstanceQ q(LinearOrder $α') let _i ← synthInstanceQ q(IsStrictOrderedRing $α') assertInstancesCommute @@ -89,10 +89,10 @@ theorem int_ceil_pos [Ring α] [LinearOrder α] [FloorRing α] {a : α} : 0 < a /-- Extension for the `positivity` tactic: `Int.ceil` is positive/nonnegative if its input is. -/ @[positivity ⌈_⌉] -meta def evalIntCeil : PositivityExt where eval {u α} _zα pα? e := do +meta def evalIntCeil : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℤ), ~q(@Int.ceil $α' $ir $io $j $a) => - let some _ := pα? | pure .none match ← core q(inferInstance) (some q(inferInstance)) a with | .positive pa => assertInstancesCommute diff --git a/Mathlib/Algebra/Order/Group/End.lean b/Mathlib/Algebra/Order/Group/End.lean index 92ab1280f939a9..fb0326451bc75f 100644 --- a/Mathlib/Algebra/Order/Group/End.lean +++ b/Mathlib/Algebra/Order/Group/End.lean @@ -6,10 +6,21 @@ Authors: Mario Carneiro module public import Mathlib.Algebra.Group.Defs +public import Mathlib.Order.Hom.Basic public import Mathlib.Order.RelIso.Basic +public import Mathlib.Data.FunLike.IsApply /-! # Relation isomorphisms form a group + +This file contains `Monoid` instances for `RelHom` and `OrderHom`, where multiplication is +given by composition. Likewise there is a `Group` instance for `RelIso`. Because `OrderIso` +is an abbreviation for `RelIso`, there is no need for an additional instance. + +## TODO + ++ Rename the `mul_def`/`one_def` lemmas to `mul_eq_comp`/`one_eq_id`. ++ Use the `IsMulApplyEqComp` and `IsOneApplyEqSelf` classes for `RelHom` and `RelIso`. -/ @[expose] public section @@ -87,3 +98,22 @@ theorem apply_inv_self (e : r ≃r r) (x) : e (e⁻¹ x) = x := e.apply_symm_apply x end RelIso + +namespace OrderHom + +variable [Preorder α] + +instance : Mul (α →o α) where mul f g := f.comp g +instance : One (α →o α) where one := .id +instance : IsMulApplyEqComp (α →o α) α where mul_apply_eq_comp _ _ _ := rfl +instance : IsOneApplyEqSelf (α →o α) α where one_apply_eq_self _ := rfl + +lemma mul_eq_comp (f g : α →o α) : (f * g : α →o α) = f.comp g := rfl +lemma one_eq_id : (1 : α →o α) = .id := rfl + +instance : Monoid (α →o α) where + mul_assoc f g h := by simp [DFunLike.ext_iff] + one_mul f := by simp [DFunLike.ext_iff] + mul_one f := by simp [DFunLike.ext_iff] + +end OrderHom diff --git a/Mathlib/Algebra/Order/Group/Multiset.lean b/Mathlib/Algebra/Order/Group/Multiset.lean index e55ebb61b5dbba..586d3ee1b45cce 100644 --- a/Mathlib/Algebra/Order/Group/Multiset.lean +++ b/Mathlib/Algebra/Order/Group/Multiset.lean @@ -63,6 +63,12 @@ lemma mem_nsmul {a : α} {s : Multiset α} {n : ℕ} : a ∈ n • s ↔ n ≠ 0 lemma mem_nsmul_of_ne_zero {a : α} {s : Multiset α} {n : ℕ} (h0 : n ≠ 0) : a ∈ n • s ↔ a ∈ s := by simp [*] +theorem smul_subset_self (s : Multiset α) (n : ℕ) : n • s ⊆ s := + subset_iff.mpr fun _ ↦ mem_of_mem_nsmul + +theorem subset_smul_self_of_ne_zero (s : Multiset α) {n : ℕ} (hn : n ≠ 0) : s ⊆ n • s := + subset_iff.mpr fun _ ↦ mem_nsmul_of_ne_zero hn |>.mpr + lemma nsmul_cons {s : Multiset α} (n : ℕ) (a : α) : n • (a ::ₘ s) = n • ({a} : Multiset α) + n • s := by rw [← singleton_add, nsmul_add] @@ -180,6 +186,14 @@ lemma count_nsmul (a : α) (n s) : count a (n • s) = n * count a s := by end +theorem le_card_smul_iff_subset {s t : Multiset α} : s ≤ s.card • t ↔ s ⊆ t := by + classical + refine ⟨fun hle ↦ Subset.trans (subset_of_le hle) (t.smul_subset_self s.card), ?_⟩ + refine fun hsub ↦ le_iff_count.mpr fun a ↦ ?_ + by_cases! has : a ∉ s + · simp [count_eq_zero_of_notMem has] + grw [count_le_card, count_nsmul, ← one_le_count_iff_mem.mpr <| mem_of_subset hsub has, mul_one] + -- TODO: This should be `addMonoidHom_ext` @[ext] lemma addHom_ext [AddZeroClass β] ⦃f g : Multiset α →+ β⦄ (h : ∀ x, f {x} = g {x}) : f = g := by @@ -189,14 +203,6 @@ lemma addHom_ext [AddZeroClass β] ⦃f g : Multiset α →+ β⦄ (h : ∀ x, f | cons a s ih => simp only [← singleton_add, _root_.map_add, ih, h] theorem le_smul_dedup [DecidableEq α] (s : Multiset α) : ∃ n : ℕ, s ≤ n • dedup s := - ⟨(s.map fun a => count a s).fold max 0, - le_iff_count.2 fun a => by - rw [count_nsmul]; by_cases h : a ∈ s - · grw [← one_le_count_iff_mem.2 <| mem_dedup.2 h] - have : count a s ≤ fold max 0 (map (fun a => count a s) (a ::ₘ erase s a)) := by - simp - rw [cons_erase h] at this - simpa [mul_succ] using this - · simp [count_eq_zero.2 h, Nat.zero_le]⟩ + ⟨s.card, le_card_smul_iff_subset.mpr s.subset_dedup⟩ end Multiset diff --git a/Mathlib/Algebra/Order/Group/Pointwise/Interval.lean b/Mathlib/Algebra/Order/Group/Pointwise/Interval.lean index 7efe689aefe6b4..1101a11f3267ee 100644 --- a/Mathlib/Algebra/Order/Group/Pointwise/Interval.lean +++ b/Mathlib/Algebra/Order/Group/Pointwise/Interval.lean @@ -898,10 +898,6 @@ theorem Ici_pow_eq {a : α} : | 1, _ => by simp | n + 2, _ => by simp [pow_succ _ n.succ, Ici_pow_eq, Ici_mul_Ici_eq] -omit [MulRightMono α] in -@[to_additive] -lemma Ici_one_eq_univ : Set.Ici (1 : α) = Set.univ := by aesop - end CanonicallyOrdered end Set diff --git a/Mathlib/Algebra/Order/Group/PosPart.lean b/Mathlib/Algebra/Order/Group/PosPart.lean index 384a848a2ec75d..6f94e2f0ebf43a 100644 --- a/Mathlib/Algebra/Order/Group/PosPart.lean +++ b/Mathlib/Algebra/Order/Group/PosPart.lean @@ -46,8 +46,8 @@ variable {α : Type*} section Lattice variable [Lattice α] -section Group -variable [Group α] {a b : α} +section DivInvMonoid +variable [DivInvMonoid α] {a b : α} /-- The *positive part* of an element `a` in a lattice ordered group is `a ⊔ 1`, denoted `a⁺ᵐ`. -/ @[to_additive @@ -72,11 +72,11 @@ instance instLeOnePart : LeOnePart α where @[to_additive (attr := simp high)] lemma oneLePart_one : (1 : α)⁺ᵐ = 1 := sup_idem _ -@[to_additive (attr := simp)] lemma leOnePart_one : (1 : α)⁻ᵐ = 1 := by simp [leOnePart] - -@[to_additive posPart_nonneg] lemma one_le_oneLePart (a : α) : 1 ≤ a⁺ᵐ := le_sup_right +@[to_additive (attr := simp) posPart_nonneg] +lemma one_le_oneLePart (a : α) : 1 ≤ a⁺ᵐ := le_sup_right -@[to_additive negPart_nonneg] lemma one_le_leOnePart (a : α) : 1 ≤ a⁻ᵐ := le_sup_right +@[to_additive (attr := simp) negPart_nonneg] +lemma one_le_leOnePart (a : α) : 1 ≤ a⁻ᵐ := le_sup_right -- TODO: `to_additive` guesses `nonposPart` @[to_additive le_posPart] lemma le_oneLePart (a : α) : a ≤ a⁺ᵐ := le_sup_left @@ -108,12 +108,19 @@ lemma leOnePart_le_one' : a⁻ᵐ ≤ 1 ↔ a⁻¹ ≤ 1 := by simp [leOnePart] @[to_additive (attr := simp)] lemma oneLePart_inv (a : α) : a⁻¹⁺ᵐ = a⁻ᵐ := rfl -@[to_additive (attr := simp)] lemma leOnePart_inv (a : α) : a⁻¹⁻ᵐ = a⁺ᵐ := by - simp [oneLePart, leOnePart] - @[to_additive] lemma oneLePart_max (a b : α) : (max a b)⁺ᵐ = max a⁺ᵐ b⁺ᵐ := by simp [oneLePart, sup_sup_distrib_right] +end DivInvMonoid + +section Group +variable [Group α] {a b : α} + +@[to_additive (attr := simp)] lemma leOnePart_one : (1 : α)⁻ᵐ = 1 := by simp [leOnePart] + +@[to_additive (attr := simp)] lemma leOnePart_inv (a : α) : a⁻¹⁻ᵐ = a⁺ᵐ := by + simp [oneLePart, leOnePart] + section MulLeftMono variable [MulLeftMono α] diff --git a/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean b/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean index 94095610485d23..7c61a24d934051 100644 --- a/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean +++ b/Mathlib/Algebra/Order/GroupWithZero/Canonical.lean @@ -58,7 +58,7 @@ instance (priority := 100) LinearOrderedCommMonoidWithZero.toMulPosStrictMono : -- See note [lower instance priority] instance (priority := 100) LinearOrderedCommMonoidWithZero.toIsOrderedMonoid : - IsOrderedMonoid α where + IsOrderedMonoid α where mul_le_mul_left a b hab c := by obtain rfl | hc := eq_or_ne c 0 · simp @@ -386,6 +386,10 @@ theorem le_ofAdd_iff a ≤ ofAdd b ↔ toAdd (unzero ha) ≤ b := ⟨toAdd_unzero_le_of_lt_ofAdd ha, le_ofAdd_of_toAdd_unzero_le ha⟩ +lemma toAdd_unzero_eq_iff {α : Type*} {a : WithZero (Multiplicative α)} (h : a ≠ 0) + (b : α) : (WithZero.unzero h).toAdd = b ↔ a = Multiplicative.ofAdd b := + ⟨fun k ↦ by subst k; exact (coe_unzero h).symm, fun k ↦ by subst k; rfl⟩ + end Multiplicative end Preorder @@ -601,4 +605,52 @@ lemma le_exp_log {x : Gᵐ⁰} : · simp · rfl +section LE + +-- This section is not generated by `to_additive` because `WithOne` does not have a `LE` instance. + +variable [LE α] {x y : WithZero α} {a b : α} + +lemma le_unzeroD_iff (hx : x ≠ 0) : b ≤ x.unzeroD a ↔ b ≤ x := by + lift x to α using hx; simp + +lemma unzeroD_le_iff (hx : x = 0 → a ≤ b) : x.unzeroD a ≤ b ↔ x ≤ b := by + cases x <;> simp [hx] + +lemma unzeroD_mono (hx : x ≠ 0) (h : x ≤ y) : x.unzeroD a ≤ y.unzeroD a := by + lift x to α using hx + cases y <;> simp_all + +end LE + +section LT + +variable [LT α] {x y : WithZero α} {a b : α} + +lemma lt_unzeroD_iff (hx : x ≠ 0) : b < x.unzeroD a ↔ b < x := by + lift x to α using hx; simp + +lemma unzeroD_lt_iff (hx : x = 0 → a < b) : x.unzeroD a < b ↔ x < b := by + cases x <;> simp [hx] + +end LT + +section Preorder + +variable [Preorder α] {x y : WithZero α} {a b : α} + +theorem le_coe_unzeroD (x : WithZero α) (b : α) : x ≤ x.unzeroD b := by cases x <;> simp + +end Preorder + +section PartialOrder + +variable [PartialOrder α] {x y : WithZero α} {a b : α} + +lemma le_unzeroD (hy : b ≤ y) : b ≤ y.unzeroD a := by + have hne : y ≠ 0 := ne_bot_of_le_ne_bot WithZero.coe_ne_zero hy + rwa [le_unzeroD_iff hne] + +end PartialOrder + end WithZero diff --git a/Mathlib/Algebra/Order/Interval/Basic.lean b/Mathlib/Algebra/Order/Interval/Basic.lean index 620b79a1c6fa82..b833e2ff856d15 100644 --- a/Mathlib/Algebra/Order/Interval/Basic.lean +++ b/Mathlib/Algebra/Order/Interval/Basic.lean @@ -660,10 +660,10 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: The length of an interval is always nonnegative. -/ @[positivity NonemptyInterval.length _] meta def evalNonemptyIntervalLength : PositivityExt where - eval {u α} _ pα? e := do + eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do let ~q(@NonemptyInterval.length _ $ig $ipo $a) := e | throwError "not NonemptyInterval.length" - let some _ := pα? | pure .none let _i ← synthInstanceQ q(IsOrderedAddMonoid $α) assertInstancesCommute return .nonnegative q(NonemptyInterval.length_nonneg $a) @@ -671,9 +671,9 @@ meta def evalNonemptyIntervalLength : PositivityExt where /-- Extension for the `positivity` tactic: The length of an interval is always nonnegative. -/ @[positivity Interval.length _] meta def evalIntervalLength : PositivityExt where - eval {u α} _ pα? e := do + eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do let ~q(@Interval.length _ $ig $ipo $a) := e | throwError "not Interval.length" - let some _ := pα? | pure .none let _i ← synthInstanceQ q(IsOrderedAddMonoid $α) assumeInstancesCommute return .nonnegative q(Interval.length_nonneg $a) diff --git a/Mathlib/Algebra/Order/Module/Algebra.lean b/Mathlib/Algebra/Order/Module/Algebra.lean deleted file mode 100644 index 5ce7dabc6d718e..00000000000000 --- a/Mathlib/Algebra/Order/Module/Algebra.lean +++ /dev/null @@ -1,14 +0,0 @@ -/- -Copyright (c) 2020 Kim Morrison. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Kim Morrison --/ -module -- shake: keep-all - -public import Mathlib.Algebra.Order.Algebra - -deprecated_module (since := "2025-12-16") - -public section - -@[deprecated (since := "2025-12-16")] alias algebraMap_monotone := algebraMap_mono diff --git a/Mathlib/Algebra/Order/Module/Field.lean b/Mathlib/Algebra/Order/Module/Field.lean index f1575550df9058..e09bbc4544fd08 100644 --- a/Mathlib/Algebra/Order/Module/Field.lean +++ b/Mathlib/Algebra/Order/Module/Field.lean @@ -103,8 +103,8 @@ end Module.IsTorsionFree /-- Positivity extension for scalar multiplication. -/ @[positivity HSMul.hSMul _ _] -meta def evalSMul : PositivityExt where eval {_u α} zα pα? (e : Q($α)) := do - let some pα := pα? | pure .none +meta def evalSMul : PositivityExt where eval {_u α} zα pα? (e : Q($α)) := + match pα? with | none => pure .none | some pα => do let .app (.app (.app (.app (.app (.app (.const ``HSMul.hSMul [u1, _, _]) (β : Q(Type u1))) _) _) _) (a : Q($β))) (b : Q($α)) ← whnfR e | throwError "failed to match hSMul" diff --git a/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean b/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean index 66c74dc9c18512..d0ba27e66c2599 100644 --- a/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean +++ b/Mathlib/Algebra/Order/Monoid/Canonical/Basic.lean @@ -15,15 +15,6 @@ public import Mathlib.Algebra.Order.Sub.Unbundled.Basic public section -namespace Finset -variable {ι α : Type*} [AddCommMonoid α] [LinearOrder α] [OrderBot α] [CanonicallyOrderedAdd α] - {s : Finset ι} {f : ι → α} - -@[simp] lemma sup_eq_zero : s.sup f = 0 ↔ ∀ i ∈ s, f i = 0 := by simp [← bot_eq_zero'] -@[simp] lemma sup'_eq_zero (hs) : s.sup' hs f = 0 ↔ ∀ i ∈ s, f i = 0 := by simp [sup'_eq_sup] - -end Finset - namespace Set variable {α : Type*} [AddCommMonoid α] [PartialOrder α] [CanonicallyOrderedAdd α] [Sub α] [OrderedSub α] {β : Type*} {f : α → β} {k : α} diff --git a/Mathlib/Algebra/Order/Monoid/Unbundled/TypeTags.lean b/Mathlib/Algebra/Order/Monoid/Unbundled/TypeTags.lean index eaca7d92a6fd20..373d81cb62a1e2 100644 --- a/Mathlib/Algebra/Order/Monoid/Unbundled/TypeTags.lean +++ b/Mathlib/Algebra/Order/Monoid/Unbundled/TypeTags.lean @@ -15,53 +15,53 @@ public section variable {α : Type*} -instance : ∀ [LE α], LE (Multiplicative α) := - fun {inst} => inst +instance [LE α] : LE (Multiplicative α) := + inferInstanceAs <| LE α -instance : ∀ [LE α], LE (Additive α) := - fun {inst} => inst +instance [LE α] : LE (Additive α) := + inferInstanceAs <| LE α -instance : ∀ [LT α], LT (Multiplicative α) := - fun {inst} => inst +instance [LT α] : LT (Multiplicative α) := + inferInstanceAs <| LT α -instance : ∀ [LT α], LT (Additive α) := - fun {inst} => inst +instance [LT α] : LT (Additive α) := + inferInstanceAs <| LT α -instance Multiplicative.preorder : ∀ [Preorder α], Preorder (Multiplicative α) := - fun {inst} => inst +instance Multiplicative.preorder [Preorder α] : Preorder (Multiplicative α) := + inferInstanceAs <| Preorder α -instance Additive.preorder : ∀ [Preorder α], Preorder (Additive α) := - fun {inst} => inst +instance Additive.preorder [Preorder α] : Preorder (Additive α) := + inferInstanceAs <| Preorder α -instance Multiplicative.partialOrder : ∀ [PartialOrder α], PartialOrder (Multiplicative α) := - fun {inst} => inst +instance Multiplicative.partialOrder [PartialOrder α] : PartialOrder (Multiplicative α) := + inferInstanceAs <| PartialOrder α -instance Additive.partialOrder : ∀ [PartialOrder α], PartialOrder (Additive α) := - fun {inst} => inst +instance Additive.partialOrder [PartialOrder α] : PartialOrder (Additive α) := + inferInstanceAs <| PartialOrder α -instance Multiplicative.linearOrder : ∀ [LinearOrder α], LinearOrder (Multiplicative α) := - fun {inst} => inst +instance Multiplicative.linearOrder [LinearOrder α] : LinearOrder (Multiplicative α) := + inferInstanceAs <| LinearOrder α -instance Additive.linearOrder : ∀ [LinearOrder α], LinearOrder (Additive α) := - fun {inst} => inst +instance Additive.linearOrder [LinearOrder α] : LinearOrder (Additive α) := + inferInstanceAs <| LinearOrder α -instance Multiplicative.orderBot [LE α] : ∀ [OrderBot α], OrderBot (Multiplicative α) := - fun {inst} => inst +instance Multiplicative.orderBot [LE α] [OrderBot α] : OrderBot (Multiplicative α) := + inferInstanceAs <| OrderBot α -instance Additive.orderBot [LE α] : ∀ [OrderBot α], OrderBot (Additive α) := - fun {inst} => inst +instance Additive.orderBot [LE α] [OrderBot α] : OrderBot (Additive α) := + inferInstanceAs <| OrderBot α -instance Multiplicative.orderTop [LE α] : ∀ [OrderTop α], OrderTop (Multiplicative α) := - fun {inst} => inst +instance Multiplicative.orderTop [LE α] [OrderTop α] : OrderTop (Multiplicative α) := + inferInstanceAs <| OrderTop α -instance Additive.orderTop [LE α] : ∀ [OrderTop α], OrderTop (Additive α) := - fun {inst} => inst +instance Additive.orderTop [LE α] [OrderTop α] : OrderTop (Additive α) := + inferInstanceAs <| OrderTop α -instance Multiplicative.boundedOrder [LE α] : ∀ [BoundedOrder α], BoundedOrder (Multiplicative α) := - fun {inst} => inst +instance Multiplicative.boundedOrder [LE α] [BoundedOrder α] : BoundedOrder (Multiplicative α) := + inferInstanceAs <| BoundedOrder α -instance Additive.boundedOrder [LE α] : ∀ [BoundedOrder α], BoundedOrder (Additive α) := - fun {inst} => inst +instance Additive.boundedOrder [LE α] [BoundedOrder α] : BoundedOrder (Additive α) := + inferInstanceAs <| BoundedOrder α instance Multiplicative.existsMulOfLe [Add α] [LE α] [ExistsAddOfLE α] : ExistsMulOfLE (Multiplicative α) := diff --git a/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean b/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean index b2576064241f69..54a80da3c58f50 100644 --- a/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean +++ b/Mathlib/Algebra/Order/Monoid/Unbundled/WithTop.lean @@ -264,8 +264,10 @@ instance addMonoid : AddMonoid (WithTop α) where | (a : α), n => ↑(n • a) | ⊤, 0 => 0 | ⊤, _n + 1 => ⊤ - nsmul_zero a := by cases a <;> simp [zero_nsmul] - nsmul_succ n a := by cases a <;> cases n <;> simp [succ_nsmul, coe_add] + nsmul_zero a := by simp_rw [HSMul.hSMul, SMul.smul]; cases a <;> simp [zero_nsmul] + nsmul_succ n a := by + simp_rw [HSMul.hSMul, SMul.smul] + cases a <;> cases n <;> simp [succ_nsmul, coe_add] @[simp, norm_cast] lemma coe_nsmul (a : α) (n : ℕ) : ↑(n • a) = n • (a : WithTop α) := rfl diff --git a/Mathlib/Algebra/Order/Positive/Field.lean b/Mathlib/Algebra/Order/Positive/Field.lean index 2c32175910675c..d2eda796258759 100644 --- a/Mathlib/Algebra/Order/Positive/Field.lean +++ b/Mathlib/Algebra/Order/Positive/Field.lean @@ -35,8 +35,10 @@ instance : Pow { x : K // 0 < x } ℤ := theorem coe_zpow (x : { x : K // 0 < x }) (n : ℤ) : ↑(x ^ n) = (x : K) ^ n := rfl -instance : CommGroup { x : K // 0 < x } := - { Positive.commMonoid with - inv_mul_cancel := fun a => Subtype.ext <| inv_mul_cancel₀ a.2.ne' } +instance : CommGroup { x : K // 0 < x } where + inv_mul_cancel a := Subtype.ext <| inv_mul_cancel₀ a.2.ne' + zpow_zero' x := Subtype.ext <| zpow_zero _ + zpow_succ' n x := Subtype.ext <| DivInvMonoid.zpow_succ' _ _ + zpow_neg' n x := Subtype.ext <| DivInvMonoid.zpow_neg' _ _ end Positive diff --git a/Mathlib/Algebra/Order/Ring/Archimedean.lean b/Mathlib/Algebra/Order/Ring/Archimedean.lean index ad71ce19de201b..333b72c4ef4ae7 100644 --- a/Mathlib/Algebra/Order/Ring/Archimedean.lean +++ b/Mathlib/Algebra/Order/Ring/Archimedean.lean @@ -101,7 +101,6 @@ instance : AddCommMonoid (ArchimedeanClass R) where add_assoc := private add_assoc' zero_add := private zero_add' add_zero x := private add_comm x _ ▸ zero_add' x - nsmul n x := n • x nsmul_zero x := by induction x with | mk x => rw [← mk_pow, pow_zero, mk_one] nsmul_succ n x := by induction x with | mk x => rw [← mk_pow, pow_succ, mk_mul, mk_pow] @@ -308,7 +307,6 @@ noncomputable instance : LinearOrderedAddCommGroupWithTop (ArchimedeanClass R) w add_neg_cancel_of_ne_top x h := by induction x with | mk x simp [← mk_inv, ← mk_mul, mul_inv_cancel₀ (mk_eq_top_iff.not.1 h)] - zsmul n x := n • x zsmul_zero' x := by induction x with | mk x => rw [← mk_zpow, zpow_zero, mk_one] zsmul_succ' := by exact zsmul_succ' zsmul_neg' n x := by diff --git a/Mathlib/Algebra/Order/Ring/GeomSum.lean b/Mathlib/Algebra/Order/Ring/GeomSum.lean index a0129ce9cfe784..0fcf496eb09ac5 100644 --- a/Mathlib/Algebra/Order/Ring/GeomSum.lean +++ b/Mathlib/Algebra/Order/Ring/GeomSum.lean @@ -84,7 +84,7 @@ lemma geom_sum_alternating_of_lt_neg_one (hx : x + 1 < 0) (hn : 1 < n) : split_ifs at ihn ⊢ with hn' · rw [lt_add_iff_pos_left] exact mul_pos_of_neg_of_neg hx0 ihn - · grw [← hx] + · grw [← hx.le] gcongr simpa only [mul_one] using mul_lt_mul_of_neg_left ihn hx0 diff --git a/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean b/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean index 92d41588b31803..6de7d61e93f446 100644 --- a/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean +++ b/Mathlib/Algebra/Order/Ring/IsNonarchimedean.lean @@ -118,7 +118,7 @@ theorem add_eq_max_of_ne {F α : Type*} [AddGroup α] [FunLike F α R] /- TODO: Remove the funlike conditions on the lemmas required for add_max_of_ne, this will allow us to remove the CommGroup part in the below which is unnecessary. -/ -lemma add_eq_max_of_ne' {α S : Type*} [Semiring S] [LinearOrder S] [AddCommGroup α] +lemma add_eq_max_of_ne' {α S : Type*} [LinearOrder S] [AddCommGroup α] (f : α → S) (fna : IsNonarchimedean f) (Neg : ∀ a, f a = f (-a)) {a b : α} (hne : f a ≠ f b) : f (a + b) = max (f a) (f b) := by wlog hab : f a > f b generalizing a b with H @@ -223,6 +223,7 @@ theorem finset_powerset_image_add [IsStrictOrderedRing R] g (powersetCard (s.card - m) s) exact ⟨⟨b, hb_in (powersetCard_nonempty.mpr (Nat.sub_le s.card m))⟩, hb⟩ +omit [Semiring R] in lemma apply_sum_eq_of_lt {α β : Type*} [AddCommGroup α] {f : α → R} (fna : IsNonarchimedean f) (f_neg : ∀ a, f a = f (-a)) {s : Finset β} {l : β → α} {k : β} (hk : k ∈ s) (hmax : ∀ j ∈ s, j ≠ k → f (l j) < f (l k)) : f (∑ i ∈ s, l i) = f (l k) := by diff --git a/Mathlib/Algebra/Order/Ring/WithTop.lean b/Mathlib/Algebra/Order/Ring/WithTop.lean index 0ca11df2f6c5c6..a7a8bc4d82a7bd 100644 --- a/Mathlib/Algebra/Order/Ring/WithTop.lean +++ b/Mathlib/Algebra/Order/Ring/WithTop.lean @@ -173,8 +173,8 @@ instance instMonoidWithZero : MonoidWithZero (WithTop α) where | (a : α), n => ↑(a ^ n) | ⊤, 0 => 1 | ⊤, _n + 1 => ⊤ - npow_zero a := by cases a <;> simp - npow_succ n a := by cases n <;> cases a <;> simp [pow_succ] + npow_zero a := by simp_rw [HPow.hPow, Pow.pow]; cases a <;> simp + npow_succ n a := by simp_rw [HPow.hPow, Pow.pow]; cases n <;> cases a <;> simp [pow_succ] @[simp, norm_cast] lemma coe_pow (a : α) (n : ℕ) : (↑(a ^ n) : WithTop α) = a ^ n := rfl diff --git a/Mathlib/Algebra/Order/Star/Basic.lean b/Mathlib/Algebra/Order/Star/Basic.lean index 9aba3e1e74e5d3..5ba1bd85cc6b43 100644 --- a/Mathlib/Algebra/Order/Star/Basic.lean +++ b/Mathlib/Algebra/Order/Star/Basic.lean @@ -81,6 +81,24 @@ class StarOrderedRing (R : Type*) [NonUnitalSemiring R] [PartialOrder R] [StarRi le_iff : ∀ x y : R, x ≤ y ↔ ∃ p, p ∈ AddSubmonoid.closure (Set.range fun s => star s * s) ∧ y = x + p +/-- A class to encode that self-adjoint elements may be expressed as the +difference of nonnegative elements. This is satisfied by any type with a +`NonUnitalContinuousFunctionalCalculus ℝ A IsSelfAdjoint` instance. +However, it can also be satisfied by continuous linear functionals equipped +with the intrinsic star operation. + +This type class can be used to guarantee `PositiveLinearMap` is a `StarHomClass`. -/ +class SelfAdjointDecompose (R : Type*) [AddGroup R] [Star R] + [PartialOrder R] where + /-- Every self-adjoint element is the difference of nonnegative elements. -/ + exists_nonneg_sub_nonneg {a : R} (ha : IsSelfAdjoint a) : + ∃ (b c : R), 0 ≤ b ∧ 0 ≤ c ∧ a = b - c + +lemma IsSelfAdjoint.exists_nonneg_sub_nonneg {R : Type*} [AddGroup R] [Star R] + [PartialOrder R] [SelfAdjointDecompose R] {a : R} (ha : IsSelfAdjoint a) : + ∃ (b c : R), 0 ≤ b ∧ 0 ≤ c ∧ a = b - c := + SelfAdjointDecompose.exists_nonneg_sub_nonneg ha + namespace StarOrderedRing section NonUnitalSemiring variable [NonUnitalSemiring R] [PartialOrder R] [StarRing R] @@ -452,6 +470,24 @@ instance (priority := 100) StarRingEquivClass.instOrderIsoClass [EquivLike F R S rw [← f_inv_f x, ← f_inv_f y] exact NonUnitalStarRingHom.map_le_map_of_map_star f_inv h +/-- While `IsSelfAdjoint.map` assumes the map is star-preserving, this lemma instead assumes the +map is an order-preserving additive map from a space where self-adjoint elements can be expressed as +differences of nonnegative elemens, and whose codomain is a star-ordered ring. When such maps are +linear over `ℂ`, they are also star-preserving, and this lemma is used to establish that one by +splitting into real and imaginary parts. -/ +@[aesop safe apply (rule_sets := [CStarAlgebra])] +lemma IsSelfAdjoint.map' {F E R : Type*} [AddCommGroup E] [PartialOrder E] [StarAddMonoid E] + [NonUnitalRing R] [PartialOrder R] [StarRing R] [StarOrderedRing R] + [SelfAdjointDecompose E] [FunLike F E R] [OrderHomClass F E R] [AddMonoidHomClass F E R] + {a : E} (ha : IsSelfAdjoint a) (f : F) : + IsSelfAdjoint (f a) := by + obtain ⟨b, c, hb, hc, rfl⟩ := ha.exists_nonneg_sub_nonneg + have h₁ := OrderHomClass.mono f hb + have h₂ := OrderHomClass.mono f hc + cfc_tac + +@[deprecated (since := "2026-06-12")] alias map_isSelfAdjoint := IsSelfAdjoint.map' + end OrderClass instance Nat.instStarOrderedRing : StarOrderedRing ℕ where diff --git a/Mathlib/Algebra/Order/Star/Real.lean b/Mathlib/Algebra/Order/Star/Real.lean index 4462717d45506f..736f21b2290e79 100644 --- a/Mathlib/Algebra/Order/Star/Real.lean +++ b/Mathlib/Algebra/Order/Star/Real.lean @@ -34,3 +34,10 @@ instance NNReal.instStarOrderedRing : StarOrderedRing ℝ≥0 := by simp only [star_trivial, mul_self_sqrt] · rintro ⟨p, -, rfl⟩ exact le_self_add + +-- for lack of a better place with the necessary imports, we place this here +-- this exists only to satisfy the trivial instances of this class +instance {R : Type*} [AddGroup R] [Lattice R] [AddLeftMono R] [Star R] : + SelfAdjointDecompose R where + exists_nonneg_sub_nonneg {a} _ := + ⟨a⁺, a⁻, posPart_nonneg a, negPart_nonneg a, by simp⟩ diff --git a/Mathlib/Algebra/Polynomial/AlgebraMap.lean b/Mathlib/Algebra/Polynomial/AlgebraMap.lean index 04d27fae9bcaaf..72b9002779afbb 100644 --- a/Mathlib/Algebra/Polynomial/AlgebraMap.lean +++ b/Mathlib/Algebra/Polynomial/AlgebraMap.lean @@ -456,12 +456,12 @@ variable (x : Π i, A i) (p : R[X]) /-- Polynomial evaluation on an indexed tuple is the indexed product of the evaluations on the components. Generalizes `Polynomial.aeval_prod` to indexed products. -/ -theorem aeval_pi (x : Π i, A i) : aeval (R := R) x = Pi.algHom R A (fun i ↦ aeval (x i)) := +theorem aeval_pi (x : Π i, A i) : aeval (R := R) x = AlgHom.pi (fun i ↦ aeval (x i)) := (funext fun i ↦ aeval_algHom (Pi.evalAlgHom R A i) x) ▸ - (Pi.algHom_comp R A (Pi.evalAlgHom R A) (aeval x)) + (AlgHom.pi_comp (Pi.evalAlgHom R A) (aeval x)) theorem aeval_pi_apply₂ (j : I) : p.aeval x j = p.aeval (x j) := - aeval_pi (R := R) x ▸ Pi.algHom_apply R A (fun i ↦ aeval (x i)) p j + aeval_pi (R := R) x ▸ AlgHom.pi_apply (fun i ↦ aeval (x i)) p j /-- Polynomial evaluation on an indexed tuple is the indexed tuple of the evaluations on the components. diff --git a/Mathlib/Algebra/Polynomial/Bivariate.lean b/Mathlib/Algebra/Polynomial/Bivariate.lean index c2064ba46b87fb..19e5486a598d95 100644 --- a/Mathlib/Algebra/Polynomial/Bivariate.lean +++ b/Mathlib/Algebra/Polynomial/Bivariate.lean @@ -353,6 +353,7 @@ lemma pderiv_zero_equivMvPolynomial {R : Type*} [CommRing R] (p : R[X][Y]) : simp_rw [← Polynomial.C_mul_X_pow_eq_monomial] simp [map_nsmul] +set_option linter.dupNamespace false in @[deprecated (since := "2025-12-09")] alias Polynomial.Bivariate.pderiv_zero_equivMvPolynomial := pderiv_zero_equivMvPolynomial @@ -367,6 +368,7 @@ lemma pderiv_one_equivMvPolynomial (p : R[X][Y]) : simp_rw [← Polynomial.C_mul_X_pow_eq_monomial] simp [derivative_pow] +set_option linter.dupNamespace false in @[deprecated (since := "2025-12-09")] alias Polynomial.Bivariate.pderiv_one_equivMvPolynomial := pderiv_one_equivMvPolynomial diff --git a/Mathlib/Algebra/Polynomial/Degree/Defs.lean b/Mathlib/Algebra/Polynomial/Degree/Defs.lean index 2baa8b38271173..3119fcb7074168 100644 --- a/Mathlib/Algebra/Polynomial/Degree/Defs.lean +++ b/Mathlib/Algebra/Polynomial/Degree/Defs.lean @@ -73,11 +73,11 @@ theorem Monic.leadingCoeff {p : R[X]} (hp : p.Monic) : leadingCoeff p = 1 := theorem Monic.coeff_natDegree {p : R[X]} (hp : p.Monic) : p.coeff p.natDegree = 1 := hp -@[simp] +@[simp, grind =] theorem degree_zero : degree (0 : R[X]) = ⊥ := rfl -@[simp] +@[simp, grind =] theorem natDegree_zero : natDegree (0 : R[X]) = 0 := rfl @@ -161,18 +161,18 @@ theorem degree_C_lt : degree (C a) < 1 := theorem degree_one_le : degree (1 : R[X]) ≤ (0 : WithBot ℕ) := by rw [← C_1]; exact degree_C_le -@[simp] +@[simp, grind =] theorem natDegree_C (a : R) : natDegree (C a) = 0 := by by_cases ha : a = 0 · have : C a = 0 := by rw [ha, C_0] rw [natDegree, degree_eq_bot.2 this, WithBot.unbotD_bot] · rw [natDegree, degree_C ha, WithBot.unbotD_zero] -@[simp] +@[simp, grind =] theorem natDegree_one : natDegree (1 : R[X]) = 0 := natDegree_C 1 -@[simp] +@[simp, grind =] theorem natDegree_natCast (n : ℕ) : natDegree (n : R[X]) = 0 := by simp only [← C_eq_natCast, natDegree_C] diff --git a/Mathlib/Algebra/Polynomial/Eval/Defs.lean b/Mathlib/Algebra/Polynomial/Eval/Defs.lean index 6cfdf3fe6d567d..c42ff574f95219 100644 --- a/Mathlib/Algebra/Polynomial/Eval/Defs.lean +++ b/Mathlib/Algebra/Polynomial/Eval/Defs.lean @@ -219,6 +219,11 @@ def eval₂RingHom (f : R →+* S) (x : S) : R[X] →+* S := theorem coe_eval₂RingHom (f : R →+* S) (x) : ⇑(eval₂RingHom f x) = eval₂ f x := rfl +@[simp] +theorem eval₂RingHom_comp_C (f : R →+* S) (x : S) : (eval₂RingHom f x).comp C = f := by + ext + simp + theorem eval₂_pow (n : ℕ) : (p ^ n).eval₂ f x = p.eval₂ f x ^ n := (eval₂RingHom _ _).map_pow _ _ diff --git a/Mathlib/Algebra/Polynomial/Factors.lean b/Mathlib/Algebra/Polynomial/Factors.lean deleted file mode 100644 index b445b2271cfa6e..00000000000000 --- a/Mathlib/Algebra/Polynomial/Factors.lean +++ /dev/null @@ -1,6 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.Tactic.NormNum -public import Mathlib.Tactic.Positivity - -deprecated_module (since := "2025-12-16") diff --git a/Mathlib/Algebra/Polynomial/Mirror.lean b/Mathlib/Algebra/Polynomial/Mirror.lean index 88991a34ceb232..26234c588f95d5 100644 --- a/Mathlib/Algebra/Polynomial/Mirror.lean +++ b/Mathlib/Algebra/Polynomial/Mirror.lean @@ -76,7 +76,7 @@ theorem coeff_mirror (n : ℕ) : · rw [coeff_eq_zero_of_natDegree_lt (by rwa [mirror_natDegree])] by_cases h1 : n ≤ p.natDegree + p.natTrailingDegree · rw [revAt_le h1, coeff_eq_zero_of_lt_natTrailingDegree] - exact (tsub_lt_iff_left h1).mpr (Nat.add_lt_add_right h2 _) + grw [h2, add_tsub_cancel_left] · rw [← revAtFun_eq, revAtFun, if_neg h1, coeff_eq_zero_of_natDegree_lt h2] rw [not_lt] at h2 rw [revAt_le (h2.trans (Nat.le_add_right _ _))] diff --git a/Mathlib/Algebra/Polynomial/Roots.lean b/Mathlib/Algebra/Polynomial/Roots.lean index af24b26f063b34..c55613f8ce6cd2 100644 --- a/Mathlib/Algebra/Polynomial/Roots.lean +++ b/Mathlib/Algebra/Polynomial/Roots.lean @@ -12,6 +12,7 @@ public import Mathlib.Data.Set.Finite.Lemmas public import Mathlib.RingTheory.Coprime.Lemmas public import Mathlib.RingTheory.Localization.FractionRing public import Mathlib.SetTheory.Cardinal.Order +public import Mathlib.Order.Filter.TendstoCofinite /-! # Theory of univariate polynomials @@ -157,6 +158,15 @@ theorem eq_of_infinite_eval_eq (p q : R[X]) (h : Set.Infinite { x | eval x p = e apply eq_zero_of_infinite_isRoot simpa only [IsRoot, eval_sub, sub_eq_zero] +/-- Non-constant polynomials have finite fibres, provided the coefficients are a domain. -/ +lemma tendstoCofinite_of_natDegree_ne_zero {R : Type} [CommRing R] [IsDomain R] (p : R[X]) + (hp : p.natDegree ≠ 0) : Filter.TendstoCofinite p.eval := by + rw [Filter.tendstoCofinite_iff_finite_preimage_singleton] + intro x + by_contra! hx + obtain ⟨rfl⟩ : p = C x := p.eq_of_infinite_eval_eq (C x) (by simpa) + simp at hp + theorem roots_mul {p q : R[X]} (hpq : p * q ≠ 0) : (p * q).roots = p.roots + q.roots := by classical exact Multiset.ext.mpr fun r => by diff --git a/Mathlib/Algebra/Polynomial/Splits.lean b/Mathlib/Algebra/Polynomial/Splits.lean index ffdd56c46e4e78..8035ef9225714b 100644 --- a/Mathlib/Algebra/Polynomial/Splits.lean +++ b/Mathlib/Algebra/Polynomial/Splits.lean @@ -572,11 +572,6 @@ section variable {A B : Type*} [CommRing R] [Field A] [Algebra R A] [CommRing B] [IsDomain B] [Algebra R B] {f : R[X]} -theorem Splits.map_aroots_algebraMap [Algebra A B] [IsScalarTower R A B] - (hf : (f.map (algebraMap R A)).Splits) : - (f.aroots A).map (algebraMap A B) = f.aroots B := by - rw [← aroots_map B A, aroots, aroots, hf.roots_map] - theorem Splits.image_rootSet (hf : (f.map (algebraMap R A)).Splits) (g : A →ₐ[R] B) : g '' f.rootSet A = f.rootSet B := by classical @@ -591,6 +586,23 @@ theorem Splits.adjoin_rootSet_eq_range end +section + +variable {A B : Type*} [CommRing R] [CommRing A] [IsDomain A] [Algebra R A] [CommRing B] + [IsDomain B] [Algebra R B] [Algebra A B] [FaithfulSMul A B] [IsScalarTower R A B] {f : R[X]} + +theorem Splits.map_aroots_algebraMap (hf : (f.map (algebraMap R A)).Splits) : + (f.aroots A).map (algebraMap A B) = f.aroots B := by + rw [← aroots_map B A, aroots, aroots, + hf.roots_map_of_injective (FaithfulSMul.algebraMap_injective A B)] + +theorem Splits.image_rootSet_algebraMap (hf : (f.map (algebraMap R A)).Splits) : + (algebraMap A B) '' f.rootSet A = f.rootSet B := by + classical + rw [rootSet, ← Finset.coe_image, ← Multiset.toFinset_map, hf.map_aroots_algebraMap, ← rootSet] + +end + variable [Field R] {f g : R[X]} theorem Splits.dvd_of_roots_le_roots (hp : f.Splits) (hp0 : f ≠ 0) (hq : f.roots ≤ g.roots) : diff --git a/Mathlib/Algebra/Prime/Lemmas.lean b/Mathlib/Algebra/Prime/Lemmas.lean index 704735cad894a0..0095606c2e6a93 100644 --- a/Mathlib/Algebra/Prime/Lemmas.lean +++ b/Mathlib/Algebra/Prime/Lemmas.lean @@ -62,6 +62,21 @@ theorem MulEquiv.prime_iff {E : Type*} [EquivLike E M N] [MulEquivClass E M N] ( end Map +variable {x y : M} + +theorem prime_units_mul (u : Mˣ) : Prime (↑u * y) ↔ Prime y := by simp [Prime] + +theorem prime_isUnit_mul (h : IsUnit x) : Prime (x * y) ↔ Prime y := + let ⟨u, hu⟩ := h + hu ▸ prime_units_mul u + +theorem prime_mul_units (u : Mˣ) : Prime (y * ↑u) ↔ Prime y := by + rw [mul_comm, prime_units_mul] + +theorem prime_mul_isUnit (h : IsUnit x) : Prime (y * x) ↔ Prime y := + let ⟨u, hu⟩ := h + hu ▸ prime_mul_units u + end Prime section IsCancelMulZero diff --git a/Mathlib/Algebra/Quaternion.lean b/Mathlib/Algebra/Quaternion.lean index 4c57a1cd268505..83aaf47e2793c9 100644 --- a/Mathlib/Algebra/Quaternion.lean +++ b/Mathlib/Algebra/Quaternion.lean @@ -471,7 +471,7 @@ theorem algebraMap_injective : (algebraMap R ℍ[R,c₁,c₂,c₃] : _ → _).In fun _ _ ↦ by simp [algebraMap_eq] instance : IsTorsionFree R ℍ[R,c₁,c₂,c₃] := - (addEquivProd ..).injective.moduleIsTorsionFree _ fun _ _ ↦ rfl + (addEquivProd ..).injective.moduleIsTorsionFree _ fun _ _ ↦ rfl section diff --git a/Mathlib/Algebra/Ring/Action/Group.lean b/Mathlib/Algebra/Ring/Action/Group.lean index d5dce7f17fa431..91e4cd7961ba25 100644 --- a/Mathlib/Algebra/Ring/Action/Group.lean +++ b/Mathlib/Algebra/Ring/Action/Group.lean @@ -7,6 +7,7 @@ module public import Mathlib.Algebra.GroupWithZero.Action.Basic public import Mathlib.Algebra.Ring.Action.Basic +public import Mathlib.Algebra.Ring.Aut public import Mathlib.Algebra.Ring.Equiv /-! @@ -25,7 +26,24 @@ variable (R : Type*) [Semiring R] /-- Each element of the group defines a semiring isomorphism. -/ @[simps!] -def MulSemiringAction.toRingEquiv [MulSemiringAction G R] (x : G) : R ≃+* R := - { DistribMulAction.toAddEquiv R x, MulSemiringAction.toRingHom G R x with } +def MulSemiringAction.toRingEquiv [MulSemiringAction G R] : G →* (R ≃+* R) where + toFun x := { DistribMulAction.toAddEquiv R x, MulSemiringAction.toRingHom G R x with } + map_one' := by ext; simp + map_mul' x y := by ext; simp [mul_smul] + +@[deprecated (since := "2026-06-19")] alias MulSemiringAction.toRingEquiv_apply := +MulSemiringAction.toRingEquiv_apply_apply + +@[deprecated (since := "2026-06-19")] alias MulSemiringAction.toRingEquiv_symm_apply := +MulSemiringAction.toRingEquiv_apply_symm_apply + +instance : MulSemiringAction (R ≃+* R) R where + smul := (· ·) + mul_smul _ _ _ := rfl + one_smul _ := rfl + smul_zero := map_zero + smul_one := map_one + smul_add := map_add + smul_mul := map_mul end Semiring diff --git a/Mathlib/Algebra/Ring/MinimalAxioms.lean b/Mathlib/Algebra/Ring/MinimalAxioms.lean index 77dcc4cb9c8d32..984584c77b6929 100644 --- a/Mathlib/Algebra/Ring/MinimalAxioms.lean +++ b/Mathlib/Algebra/Ring/MinimalAxioms.lean @@ -69,8 +69,7 @@ abbrev Ring.ofMinimalAxioms {R : Type u} mul_assoc := mul_assoc one_mul := one_mul mul_one := mul_one - neg_add_cancel := neg_add_cancel - zsmul := (· • ·) } + neg_add_cancel := neg_add_cancel } /-- Define a `CommRing` structure on a Type by proving a minimized set of axioms. Note that this uses the default definitions for `npow`, `nsmul`, `zsmul` and `sub` diff --git a/Mathlib/Algebra/Ring/Periodic.lean b/Mathlib/Algebra/Ring/Periodic.lean index 9b86104f1dc820..71fcf017b9bc3e 100644 --- a/Mathlib/Algebra/Ring/Periodic.lean +++ b/Mathlib/Algebra/Ring/Periodic.lean @@ -41,7 +41,7 @@ namespace Function /-- A function `f` is said to be `Periodic` with period `c` if for all `x`, `f (x + c) = f x`. -/ -@[simp] +@[simp, wikidata Q184743] def Periodic [Add α] (f : α → β) (c : α) : Prop := ∀ x : α, f (x + c) = f x @@ -415,4 +415,11 @@ theorem Antiperiodic.mul [Add α] [Mul β] [HasDistribNeg β] (hf : Antiperiodic theorem Antiperiodic.div [Add α] [DivisionMonoid β] [HasDistribNeg β] (hf : Antiperiodic f c) (hg : Antiperiodic g c) : Periodic (f / g) c := by simp_all [neg_div_neg_eq] +/-- For an antiperiodic function `f` with antiperiod `c`, summing `f` over a `Finset` shifted by +`c` (via `addRightEmbedding c`) negates the sum over the original `Finset`. -/ +theorem Antiperiodic.sum_map_addRightEmbedding [Add α] [IsRightCancelAdd α] + [SubtractionCommMonoid β] (hf : Antiperiodic f c) (s : Finset α) : + ∑ k ∈ s.map (addRightEmbedding c), f k = -∑ k ∈ s, f k := by + simp [hf _] + end Function diff --git a/Mathlib/Algebra/Ring/Subring/Basic.lean b/Mathlib/Algebra/Ring/Subring/Basic.lean index 381b16158620ac..26fdd10daf025d 100644 --- a/Mathlib/Algebra/Ring/Subring/Basic.lean +++ b/Mathlib/Algebra/Ring/Subring/Basic.lean @@ -842,6 +842,11 @@ theorem domRestrict_comp_rangeRestrict (g : S →+* T) (f : R →+* S) : (g.domRestrict f.range).comp (f.rangeRestrict) = g.comp f := rfl +@[simp] +theorem range_prodMap {R' S' : Type*} [Ring R'] [Ring S'] (f : R →+* S) (g : R' →+* S') : + (f.prodMap g).range = f.range.prod g.range := + SetLike.coe_injective Set.range_prodMap + section eqLocus variable {S : Type v} [Semiring S] diff --git a/Mathlib/Algebra/Ring/Subsemiring/Basic.lean b/Mathlib/Algebra/Ring/Subsemiring/Basic.lean index 815cc588c3d5f9..0a393c2b0c391b 100644 --- a/Mathlib/Algebra/Ring/Subsemiring/Basic.lean +++ b/Mathlib/Algebra/Ring/Subsemiring/Basic.lean @@ -662,6 +662,11 @@ theorem top_prod (s : Subsemiring S) : (⊤ : Subsemiring R).prod s = s.comap (R theorem top_prod_top : (⊤ : Subsemiring R).prod (⊤ : Subsemiring S) = ⊤ := (top_prod _).trans <| comap_top _ +@[simp] +theorem _root_.RingHom.rangeS_prodMap (f : R →+* S) (g : S →+* T) : + (f.prodMap g).rangeS = Subsemiring.prod f.rangeS g.rangeS := + SetLike.coe_injective Set.range_prodMap + protected theorem center_prod : center (R × S) = prod (center R) (center S) := SetLike.coe_injective Set.center_prod diff --git a/Mathlib/Algebra/RingQuot.lean b/Mathlib/Algebra/RingQuot.lean index 6bb7fc25be788b..86352ce1c777b7 100644 --- a/Mathlib/Algebra/RingQuot.lean +++ b/Mathlib/Algebra/RingQuot.lean @@ -5,7 +5,7 @@ Authors: Kim Morrison -/ module -public import Mathlib.Algebra.Algebra.Hom +public import Mathlib.Algebra.Algebra.Equiv public import Mathlib.RingTheory.Congruence.Basic public import Mathlib.RingTheory.Ideal.Quotient.Defs public import Mathlib.RingTheory.Ideal.Span @@ -278,7 +278,6 @@ instance instRing {R : Type uR} [Ring R] (r : R → R → Prop) : Ring (RingQuot sub_eq_add_neg := by rintro ⟨⟨⟩⟩ ⟨⟨⟩⟩ simp [neg_quot, sub_quot, add_quot, sub_eq_add_neg] - zsmul := (· • ·) zsmul_zero' := by rintro ⟨⟨⟩⟩ simp [smul_quot, ← zero_quot] @@ -546,6 +545,38 @@ theorem eq_liftAlgHom_comp_mkAlgHom {s : A → A → Prop} (f : RingQuot s → f = liftAlgHom S ⟨f.comp (mkAlgHom S s), fun _ _ h ↦ congr_arg f (mkAlgHom_rel S h)⟩ := liftAlgHom_unique S (f.comp (mkAlgHom S s)) (fun _ _ h ↦ congr_arg (⇑f) (mkAlgHom_rel S h)) f rfl +open scoped Function -- required for scoped `on` notation + +variable {S} + +/-- If two `S`-algebras are `S`-equivalent and their quotients by a relation `rel` are defined, +then their quotients are also `S`-equivalent. + +(Special case of the third isomorphism theorem.) -/ +def algEquivQuotAlgEquiv (f : A ≃ₐ[S] B) (rel : A → A → Prop) : + RingQuot rel ≃ₐ[S] RingQuot (rel on f.symm) := + AlgEquiv.ofAlgHom + (RingQuot.liftAlgHom S (s := rel) + ⟨AlgHom.comp (RingQuot.mkAlgHom S (rel on f.symm)) f, + fun x y h_rel ↦ by + apply RingQuot.mkAlgHom_rel + simpa [Function.onFun]⟩) + ((RingQuot.liftAlgHom S (s := rel on f.symm) + ⟨AlgHom.comp (RingQuot.mkAlgHom S rel) f.symm, + fun x y h ↦ by apply RingQuot.mkAlgHom_rel; simpa⟩)) + (by ext b; simp) (by ext a; simp) + +/-- If two (semi)rings are equivalent and their quotients by a relation `rel` are defined, +then their quotients are also equivalent. + +(Special case of `algEquivQuotAlgEquiv` when `S = ℕ`, which in turn is a special +case of the third isomorphism theorem.) -/ +def equivQuotEquiv (f : A ≃+* B) (rel : A → A → Prop) : + RingQuot rel ≃+* RingQuot (rel on f.symm) := + let f_alg : A ≃ₐ[ℕ] B := + AlgEquiv.ofRingEquiv (f := f) (fun n ↦ by simp) + algEquivQuotAlgEquiv f_alg rel |>.toRingEquiv + end Algebra end RingQuot diff --git a/Mathlib/Algebra/SkewMonoidAlgebra/Support.lean b/Mathlib/Algebra/SkewMonoidAlgebra/Support.lean index 1ea8d1e5037c62..7940905d09c8ed 100644 --- a/Mathlib/Algebra/SkewMonoidAlgebra/Support.lean +++ b/Mathlib/Algebra/SkewMonoidAlgebra/Support.lean @@ -110,7 +110,7 @@ theorem support_mul_single_eq_image {r : k} {x : G} (rx : IsRightRegular x) end DecidableEq theorem support_mul_single [IsRightCancelMul G] (r : k) (x : G) - (hrx : ∀ g : G, ∀ y, y * g • r = 0 ↔ y = 0) : + (hrx : ∀ g : G, ∀ y, y * g • r = 0 ↔ y = 0) : (f * single x r).support = f.support.map (mulRightEmbedding x) := by classical ext a diff --git a/Mathlib/Algebra/SkewPolynomial/Basic.lean b/Mathlib/Algebra/SkewPolynomial/Basic.lean index 700145186964c9..926b3911497807 100644 --- a/Mathlib/Algebra/SkewPolynomial/Basic.lean +++ b/Mathlib/Algebra/SkewPolynomial/Basic.lean @@ -42,7 +42,7 @@ Furthermore, with this notation `φ^[n](a) = (ofAdd n) • a`, see `φ_iterate_a * `SkewPolynomial.monomial n a` is the skew polynomial `a X ^ n`. Note that `SkewPolynomial.monomial n` is defined as an `R`-linear map. * `SkewPolynomial.C a` is the constant skew polynomial `a`. Note that `C` is defined as an additive - homomorphism. + homomorphism. * `SkewPolynomial.CRingHom a` is the constant skew polynomial `a`, as a ring homomorphism. This requires to assume `[MulSemiringAction (Multiplicative ℕ) R]`. * `SkewPolynomial.X` is the skew polynomial `X`, i.e., `SkewPolynomial.monomial 1 1`. @@ -173,8 +173,7 @@ def monomial : R →ₗ[R] SkewPolynomial R := lsingle R (ofAdd n) lemma monomial_zero_right : monomial n (0 : R) = 0 := single_zero _ -lemma monomial_zero_one [MulSemiringAction (Multiplicative ℕ) R] : monomial 0 (1 : R) = 1 := - rfl +lemma monomial_zero_one : monomial 0 (1 : R) = 1 := rfl lemma monomial_def (a : R) : monomial n a = single (ofAdd n) a := rfl @@ -187,7 +186,7 @@ lemma smul_monomial {S} [Semiring S] [Module S R] (a : S) (b : R) : @[simp] lemma sum_monomial (f : SkewPolynomial R) : f.sum (fun (a : ℕ) ↦ monomial a) = f := - SkewMonoidAlgebra.sum_single _ + SkewMonoidAlgebra.sum_single _ @[simp] lemma sum_monomial_index {N} [AddCommMonoid N] {n : ℕ} {b : R} {h : ℕ → R → N} @@ -348,8 +347,7 @@ lemma coeff_monomial : coeff (monomial n a) m = if n = m then a else 0 := @[simp] lemma coeff_zero (n : ℕ) : coeff (0 : SkewPolynomial R) n = 0 := rfl -@[simp] lemma coeff_one_zero [MulSemiringAction (Multiplicative ℕ) R] : - coeff (1 : SkewPolynomial R) 0 = 1 := coeff_monomial +@[simp] lemma coeff_one_zero : coeff (1 : SkewPolynomial R) 0 = 1 := coeff_monomial lemma coeff_one [MulSemiringAction (Multiplicative ℕ) R] (n : ℕ) : coeff (1 : SkewPolynomial R) n = if 0 = n then 1 else 0 := by diff --git a/Mathlib/Algebra/Squarefree/Basic.lean b/Mathlib/Algebra/Squarefree/Basic.lean index 79091abbce8d52..af8d698b23388d 100644 --- a/Mathlib/Algebra/Squarefree/Basic.lean +++ b/Mathlib/Algebra/Squarefree/Basic.lean @@ -12,6 +12,7 @@ public import Mathlib.RingTheory.UniqueFactorizationDomain.Multiplicity /-! # Squarefree elements of monoids + An element of a monoid is squarefree when it is not divisible by any squares except the squares of units. diff --git a/Mathlib/Algebra/Star/Basic.lean b/Mathlib/Algebra/Star/Basic.lean index f37f1dc5d07931..c109f8adb81226 100644 --- a/Mathlib/Algebra/Star/Basic.lean +++ b/Mathlib/Algebra/Star/Basic.lean @@ -94,8 +94,10 @@ theorem star_inj [InvolutiveStar R] {x y : R} : star x = star y ↔ x = y := /-- `star` as an equivalence when it is involutive. -/ @[simps! apply] -protected def Equiv.Perm.star [InvolutiveStar R] : Equiv.Perm R := - star_involutive.toPerm _ +protected def Equiv.Perm.star [InvolutiveStar R] : Equiv.Perm R where + toFun := star + invFun := star + __ : Equiv.Perm R := star_involutive.toPerm _ @[simp] theorem Equiv.Perm.symm_star [InvolutiveStar R] : @@ -213,7 +215,7 @@ theorem star_div [CommGroup R] [StarMul R] (x y : R) : star (x / y) = star x / s See note [reducible non-instances]. -/ abbrev starMulOfComm {R : Type*} [CommMonoid R] : StarMul R where - star := id + star x := x star_involutive _ := rfl star_mul := mul_comm @@ -324,7 +326,8 @@ variable [CommSemiring R] [StarRing R] /-- `star` as a ring automorphism, for commutative `R`. -/ @[simps apply] -def starRingAut : RingAut R := { starAddEquiv, starMulAut (R := R) with toFun := star } +def starRingAut : RingAut R := + { starAddEquiv, starMulAut (R := R) with toFun := star, invFun := star } variable (R) in /-- `star` as a ring endomorphism, for commutative `R`. This is used to denote complex @@ -334,7 +337,10 @@ Note that this is the preferred form (over `starRingAut`, available under the sa because the notation `E →ₗ⋆[R] F` for an `R`-conjugate-linear map (short for `E →ₛₗ[starRingEnd R] F`) does not pretty-print if there is a coercion involved, as would be the case for `(↑starRingAut : R →* R)`. -/ -def starRingEnd : R →+* R := @starRingAut R _ _ +@[implicit_reducible] +def starRingEnd : R →+* R where + toFun := star + __ := (@starRingAut R _ _).toRingHom @[inherit_doc] scoped[ComplexConjugate] notation "conj" => starRingEnd _ diff --git a/Mathlib/Algebra/Star/LinearMap.lean b/Mathlib/Algebra/Star/LinearMap.lean index db4ccb754ff7ef..0249b8488b7250 100644 --- a/Mathlib/Algebra/Star/LinearMap.lean +++ b/Mathlib/Algebra/Star/LinearMap.lean @@ -63,7 +63,7 @@ instance intrinsicStarAddMonoid : StarAddMonoid (WithConv (E →ₗ[R] F)) where theorem IntrinsicStar.isSelfAdjoint_iff_map_star (f : WithConv (E →ₗ[R] F)) : IsSelfAdjoint f ↔ ∀ x, f (star x) = star (f x) := by simp_rw [IsSelfAdjoint, WithConv.ext_iff, LinearMap.ext_iff, intrinsicStar_apply, - star_eq_iff_star_eq, eq_comm] + star_eq_iff_star_eq, eq_comm] @[deprecated (since := "2025-12-09")] alias isSelfAdjoint_iff_map_star := IntrinsicStar.isSelfAdjoint_iff_map_star diff --git a/Mathlib/Algebra/Star/Unitary.lean b/Mathlib/Algebra/Star/Unitary.lean index c9164a65c0508e..3cf41580a98415 100644 --- a/Mathlib/Algebra/Star/Unitary.lean +++ b/Mathlib/Algebra/Star/Unitary.lean @@ -189,6 +189,23 @@ instance coe_isStarNormal (u : unitary R) : IsStarNormal (u : R) where lemma _root_.isStarNormal_of_mem_unitary {u : R} (hu : u ∈ unitary R) : IsStarNormal u := coe_isStarNormal ⟨u, hu⟩ +lemma commute_self_star (u : unitary R) : Commute u (star u) := by simp [commute_iff_eq] +lemma commute_star_self (u : unitary R) : Commute (star u) u := by simp [commute_iff_eq] + +lemma _root_.commute_unitary_star_self {u : R} (hu : u ∈ unitary R) : Commute (star u) u := + isStarNormal_of_mem_unitary hu |>.star_comm_self + +lemma _root_.commute_unitary_self_star {u : R} (hu : u ∈ unitary R) : Commute u (star u) := + commute_unitary_star_self hu |>.symm + +lemma _root_.commute_unitary_iff_star_left_conjugate {x u : R} (hu : u ∈ unitary R) : + Commute u x ↔ star u * x * u = x := by + simpa using! (Unitary.toUnits ⟨u, hu⟩).commute_iff_inv_mul_cancel + +lemma _root_.commute_unitary_iff_star_right_conjugate {x u : R} (hu : u ∈ unitary R) : + Commute u x ↔ u * x * star u = x := by + simpa using! (Unitary.toUnits ⟨u, hu⟩).commute_iff_mul_inv_cancel + end Monoid end Unitary diff --git a/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean b/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean index 25d846324e92f6..6dfa47f00a244d 100644 --- a/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean +++ b/Mathlib/Algebra/Star/UnitaryStarAlgAut.lean @@ -38,7 +38,7 @@ def conjStarAlgAut : unitary R →* (R ≃⋆ₐ[S] R) where dsimp [ConjAct.units_smul_def] simp [mul_assoc, ← Unitary.star_eq_inv] } map_one' := by ext; simp - map_mul' g h := by ext; simp [mul_smul] + map_mul' g h := by ext; simp @[simp] theorem conjStarAlgAut_apply (u : unitary R) (x : R) : conjStarAlgAut S R u x = u * x * (star u : R) := rfl diff --git a/Mathlib/Algebra/Symmetrized.lean b/Mathlib/Algebra/Symmetrized.lean index 87503423a78036..d2127d0905153b 100644 --- a/Mathlib/Algebra/Symmetrized.lean +++ b/Mathlib/Algebra/Symmetrized.lean @@ -183,12 +183,10 @@ theorem sym_mul_sym [Mul α] [Add α] [One α] [OfNat α 2] [Invertible (2 : α) sym a * sym b = sym (⅟2 * (a * b + b * a)) := rfl -set_option linter.existingAttributeWarning false in @[simp, to_additive existing] theorem sym_inv [Inv α] (a : α) : sym a⁻¹ = (sym a)⁻¹ := rfl -set_option linter.existingAttributeWarning false in @[simp, to_additive existing] theorem unsym_inv [Inv α] (a : αˢʸᵐ) : unsym a⁻¹ = (unsym a)⁻¹ := rfl diff --git a/Mathlib/Algebra/Vertex/HVertexOperator.lean b/Mathlib/Algebra/Vertex/HVertexOperator.lean index f997c5f9059696..4573070993920c 100644 --- a/Mathlib/Algebra/Vertex/HVertexOperator.lean +++ b/Mathlib/Algebra/Vertex/HVertexOperator.lean @@ -9,6 +9,7 @@ public import Mathlib.RingTheory.HahnSeries.Multiplication /-! # Vertex operators + In this file we introduce heterogeneous vertex operators using Hahn series. When `R = ℂ`, `V = W`, and `Γ = ℤ`, then this is the usual notion of "meromorphic left-moving 2D field". The notion we use here allows us to consider composites and scalar-multiply by multivariable Laurent series. diff --git a/Mathlib/Algebra/Vertex/VertexOperator.lean b/Mathlib/Algebra/Vertex/VertexOperator.lean index 2edd8712cec5fd..635dcc0682f0a4 100644 --- a/Mathlib/Algebra/Vertex/VertexOperator.lean +++ b/Mathlib/Algebra/Vertex/VertexOperator.lean @@ -10,6 +10,7 @@ public import Mathlib.Data.Int.Interval /-! # Vertex operators + In this file we introduce vertex operators as linear maps to Laurent series. ## Definitions diff --git a/Mathlib/AlgebraicGeometry/AffineScheme.lean b/Mathlib/AlgebraicGeometry/AffineScheme.lean index 06bc6b0c1d65fa..fb0f92073d1e68 100644 --- a/Mathlib/AlgebraicGeometry/AffineScheme.lean +++ b/Mathlib/AlgebraicGeometry/AffineScheme.lean @@ -432,6 +432,10 @@ instance isOpenImmersion_fromSpec : @[reassoc (attr := simp)] lemma isoSpec_inv_ι : hU.isoSpec.inv ≫ U.ι = hU.fromSpec := rfl +@[reassoc (attr := simp)] +lemma isoSpec_hom_fromSpec : hU.isoSpec.hom ≫ hU.fromSpec = U.ι := by + simp [← cancel_epi hU.isoSpec.inv] + @[reassoc (attr := simp)] lemma toSpecΓ_fromSpec : U.toSpecΓ ≫ hU.fromSpec = U.ι := toSpecΓ_isoSpec_inv_assoc _ _ @@ -943,52 +947,37 @@ theorem self_le_iSup_basicOpen_iff {s : Set Γ(X, U)} : end IsAffineOpen +/-- The affine open cover given by a covering family of affine opens. -/ +@[simps I₀ X f] +def Scheme.AffineOpenCover.ofIsOpenCover {X : Scheme.{u}} {ι : Type*} (U : ι → X.Opens) + (hU : IsOpenCover U) (hU' : ∀ i, IsAffineOpen (U i)) : + AffineOpenCover X where + I₀ := ι + X i := Γ(X, U i) + f i := (hU' i).fromSpec + idx x := (hU.exists_mem x).choose + covers x := + ⟨(hU' _).isoSpec.hom ⟨_, (hU.exists_mem x).choose_spec⟩, by simp [← Scheme.Hom.comp_apply]⟩ + set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in open _root_.PrimeSpectrum in /-- The restriction of `Spec.map f` to a basic open `D(r)` is isomorphic to `Spec.map` of the localization of `f` away from `r`. -/ -noncomputable -def SpecMapRestrictBasicOpenIso {R S : CommRingCat} (f : R ⟶ S) (r : R) : +noncomputable def SpecMapRestrictBasicOpenIso {R S : CommRingCat} (f : R ⟶ S) (r : R) : Arrow.mk (Spec.map f ∣_ (PrimeSpectrum.basicOpen r)) ≅ Arrow.mk (Spec.map <| CommRingCat.ofHom (Localization.awayMap f.hom r)) := by - letI e₁ : Localization.Away r ≃ₐ[R] Γ(Spec R, basicOpen r) := - IsLocalization.algEquiv (Submonoid.powers r) _ _ - letI e₂ : Localization.Away (f.hom r) ≃ₐ[S] Γ(Spec S, basicOpen (f.hom r)) := - IsLocalization.algEquiv (Submonoid.powers (f.hom r)) _ _ refine Arrow.isoMk ?_ ?_ ?_ - · exact (Spec _).isoOfEq (comap_basicOpen _ _) ≪≫ - (IsAffineOpen.Spec_basicOpen (f.hom r)).isoSpec ≪≫ Scheme.Spec.mapIso e₂.toCommRingCatIso.op - · exact (IsAffineOpen.Spec_basicOpen r).isoSpec ≪≫ Scheme.Spec.mapIso e₁.toCommRingCatIso.op - · have := AlgebraicGeometry.IsOpenImmersion.of_isLocalization - (S := (Localization.Away r)) r - rw [← cancel_mono (Spec.map (CommRingCat.ofHom (algebraMap R (Localization.Away r))))] - simp only [Arrow.mk_left, Arrow.mk_right, Scheme.isoOfEq_rfl, Iso.refl_trans, - Iso.trans_hom, Functor.mapIso_hom, Iso.op_hom, Scheme.Spec_map, Quiver.Hom.unop_op, - Arrow.mk_hom, Category.assoc, ← Spec.map_comp] - conv => - congr - · enter [2, 1]; tactic => - change _ = - (f ≫ (Scheme.ΓSpecIso S).inv ≫ (Spec S).presheaf.map (homOfLE le_top).op) - ext - simp only [Localization.awayMap, IsLocalization.Away.map, - RingEquiv.toCommRingCatIso_hom, AlgEquiv.toRingEquiv_toRingHom, CommRingCat.hom_comp, - CommRingCat.hom_ofHom, RingHom.comp_apply, IsLocalization.map_eq, RingHom.coe_coe, - AlgEquiv.commutes, IsAffineOpen.algebraMap_Spec_obj] - · enter [2, 2, 1]; tactic => - change _ = (Scheme.ΓSpecIso R).inv ≫ (Spec R).presheaf.map (homOfLE le_top).op - ext - simp only [RingEquiv.toCommRingCatIso_hom, - AlgEquiv.toRingEquiv_toRingHom, CommRingCat.hom_comp, CommRingCat.hom_ofHom, - RingHom.coe_comp, RingHom.coe_coe, Function.comp_apply, AlgEquiv.commutes, - IsAffineOpen.algebraMap_Spec_obj, homOfLE_leOfHom] - simp only [IsAffineOpen.isoSpec_hom, homOfLE_leOfHom, Spec.map_comp, Category.assoc, - Scheme.Opens.toSpecΓ_SpecMap_presheaf_map_assoc, Scheme.Opens.toSpecΓ_top, - Scheme.homOfLE_ι_assoc, morphismRestrict_ι_assoc] - simp only [← SpecMap_ΓSpecIso_hom, ← Spec.map_comp, Category.assoc, Iso.inv_hom_id, - Category.comp_id, Category.id_comp] - rfl + · exact (Spec _).isoOfEq (comap_basicOpen _ _) ≪≫ basicOpenIsoSpecAway (f.hom r) + · exact basicOpenIsoSpecAway r + · have hcomp : CommRingCat.ofHom (algebraMap R (Localization.Away r)) ≫ + CommRingCat.ofHom (Localization.awayMap f.hom r) = + f ≫ CommRingCat.ofHom (algebraMap S (Localization.Away (f.hom r))) := by + ext x + simp [Localization.awayMap, IsLocalization.Away.map] + rw [← cancel_mono (Spec.map (CommRingCat.ofHom (algebraMap R _)))] + simp only [Arrow.mk_hom, Category.assoc, ← Spec.map_comp] + simp [hcomp] lemma stalkMap_injective_of_isAffine {X Y : Scheme} (f : X ⟶ Y) [IsAffine Y] (x : X) (h : ∀ g, f.stalkMap x (Y.presheaf.Γgerm (f x) g) = 0 → Y.presheaf.Γgerm (f x) g = 0) : diff --git a/Mathlib/AlgebraicGeometry/AffineSpace.lean b/Mathlib/AlgebraicGeometry/AffineSpace.lean index 2ec9cee81b52b1..c886fc0dfce1a6 100644 --- a/Mathlib/AlgebraicGeometry/AffineSpace.lean +++ b/Mathlib/AlgebraicGeometry/AffineSpace.lean @@ -457,7 +457,7 @@ lemma isIntegralHom_over_iff_isEmpty : IsIntegralHom (𝔸(n; S) ↘ S) ↔ IsEm have : (rename fun _ ↦ i).comp (uniqueAlgEquiv.{_, u} _ PUnit).symm.toAlgHom p = 0 := by simp [← hp', ← algebraMap_eq] rw [AlgHom.comp_apply, map_eq_zero_iff _ (rename_injective _ (fun _ _ _ ↦ rfl))] at this - simp only [AlgEquiv.coe_algHom, EmbeddingLike.map_eq_zero_iff] at this + simp only [AlgEquiv.coe_toAlgHom, EmbeddingLike.map_eq_zero_iff] at this simp [this] at hp · rintro (_ | _) <;> infer_instance diff --git a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean index 105ba3a560eaad..0bfee543aed3b0 100644 --- a/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean +++ b/Mathlib/AlgebraicGeometry/AffineTransitionLimit.lean @@ -1095,7 +1095,6 @@ lemma Scheme.exists_isOpenCover_and_isAffine_of_finite [IsCofiltered I] simp · rw [← hVU, ← Hom.comp_preimage, c.w] -set_option backward.isDefEq.respectTransparency false in open TopologicalSpace in include hc in /-- Suppose `{ Xᵢ }` is an inverse system of qcqs schemes with affine transition maps. @@ -1175,7 +1174,7 @@ private nonrec lemma Scheme.exists_π_app_comp_eq_of_locallyOfFinitePresentation obtain ⟨R, rfl⟩ := hS wlog hX : ∃ S, X = Spec S generalizing X · obtain ⟨i, f, hf⟩ := this (a ≫ X.isoSpec.hom) (X.isoSpec.inv ≫ f) - (by simp [ha, - Functor.map_comp]) ⟨_, rfl⟩ + (by simp [ha, -Functor.map_comp]) ⟨_, rfl⟩ exact ⟨i, f ≫ X.isoSpec.inv, by simpa [← Iso.comp_inv_eq] using! hf⟩ obtain ⟨S, rfl⟩ := hX obtain ⟨φ, rfl⟩ := Spec.map_surjective f diff --git a/Mathlib/AlgebraicGeometry/AlgebraicCycle/Basic.lean b/Mathlib/AlgebraicGeometry/AlgebraicCycle/Basic.lean new file mode 100644 index 00000000000000..22d69227a54f40 --- /dev/null +++ b/Mathlib/AlgebraicGeometry/AlgebraicCycle/Basic.lean @@ -0,0 +1,79 @@ +/- +Copyright (c) 2026 Raphael Douglas Giles. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Raphael Douglas Giles +-/ +module + +public import Mathlib.AlgebraicGeometry.Morphisms.QuasiCompact +public import Mathlib.AlgebraicGeometry.Properties +public import Mathlib.Topology.LocallyFinsupp.Pushforward +public import Mathlib.AlgebraicGeometry.ResidueField + +/-! +# Algebraic Cycles + +In this file we define algebraic cycles on a scheme `X` with coefficients in a type `R` and provide +some basic API for working with them. We define an algebraic cycle on a scheme `X` with +coefficients in a type `R` to be functions `c : X → R` whose support is locally finite. + +## Implementation notes + +Here we're making use of the equivalence between irreducible closed subsets of a scheme and their +generic points in order to reuse the API in `Function.locallyFinsupp`, hence the slightly +nonstandard definition. +-/ + +@[expose] public section + +namespace AlgebraicGeometry + +open CategoryTheory + +universe u v +variable {X Y : Scheme.{u}} {R : Type*} + +/-- +Algebraic cycle on a scheme `X` with coefficients in a type `Z` is just a function from `X` to `Z` +with locally finite support (see the module docstring for more details). + +Note: currently this is an abbrev to save some effort in duplicating API. This seems fine for now, +but be aware of this if there is ever an instance clash involving algebraic cycles. +-/ +@[stacks 02QR] +abbrev AlgebraicCycle (X : Scheme.{u}) (R : Type*) [Zero R] := + Function.locallyFinsupp X R + +variable (f : X ⟶ Y) [Semiring R] (c : AlgebraicCycle X R) (x : X) (z : Y) +namespace AlgebraicCycle + +/-- +Implementation detail for `AlgebraicCycle.map`: function used to define the coefficient of the +pushforward of a cycle `c` at a point `z = f x`. +-/ +@[stacks 02R3] +noncomputable def mapCoeff {N : Type*} [DecidableEq N] {Y : Scheme} (f : X ⟶ Y) (wx : X → N) + (wy : Y → N) (x : X) : ℕ := if wx x = wy (f.base x) then f.residueDegree x else 0 + +/-- +The pushforward of algebraic cycles with respect to a quasicompact morphism of schemes. The +arguments `wx` and `wy` are certain weight functions used to calculate how the weights of the +algebraic cycle should be adjusted to make the pushforward operation functorial. Typically in +applications these will be some notions of dimension or codimension. The most common notion of +dimension is `Order.height`, and the most common notion of codimension is `Order.coheight`, though +more sophisticated notions exist in the literature which are useful when sufficient +equidimensionality hypotheses cannot be assumed. +-/ +@[stacks 02R3] +noncomputable +def map [QuasiCompact f] {N : Type*} [DecidableEq N] (wx : X → N) (wy : Y → N) + (c : AlgebraicCycle X R) : AlgebraicCycle Y R := + Function.locallyFinsupp.map f (Nat.cast (R := R) <| mapCoeff f wx wy ·) f.isSpectralMap c + +@[simp] +lemma map_id {N : Type*} [DecidableEq N] (wx : X → N) (c : AlgebraicCycle X R) : + map (𝟙 _) wx wx c = c := by + apply Function.locallyFinsupp.map_id + simp [mapCoeff] + +end AlgebraicGeometry.AlgebraicCycle diff --git a/Mathlib/AlgebraicGeometry/Birational/Birational.lean b/Mathlib/AlgebraicGeometry/Birational/Birational.lean new file mode 100644 index 00000000000000..a191e2e295f390 --- /dev/null +++ b/Mathlib/AlgebraicGeometry/Birational/Birational.lean @@ -0,0 +1,292 @@ +/- +Copyright (c) 2026 Justus Springer. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Justus Springer +-/ +module + +public import Mathlib.AlgebraicGeometry.AffineSpace +public import Mathlib.AlgebraicGeometry.Birational.RationalMap + +/-! +# Birationality and Rationality of schemes. + +This file defines partial isomorphisms between schemes and uses them to formalize +birationality and rationality. + +## Main definitions + +- `Scheme.PartialIso X Y`: an isomorphism between a dense open subscheme of `X` and a + dense open subscheme of `Y`. +- `Scheme.Birational X Y`: `X` and `Y` are birational, i.e. there exists a `PartialIso X Y`. +- `Scheme.BirationalOver sX sY`: `X` and `Y` are birational over `S` via structure maps + `sX : X ⟶ S` and `sY : Y ⟶ S`. +- `Scheme.IsRationalOver sX`: `X` is rational over `S` via structure map `sX : X ⟶ S`, + i.e. birational over `S` to some affine space `𝔸(n; S)`. + +-/ + +@[expose] public section + +universe u + +open CategoryTheory + +namespace AlgebraicGeometry.Scheme + +/-- A partial isomorphism from `X` to `Y` is an isomorphism between dense open subschemes +of `X` and `Y`. -/ +structure PartialIso (X Y : Scheme.{u}) where + /-- The source open subscheme of a partial isomorphism. -/ + source : X.Opens + dense_source : Dense (source : Set X) + /-- The target open subscheme of a partial isomorphism. -/ + target : Y.Opens + dense_target : Dense (target : Set Y) + /-- The underlying isomorphism of a partial isomorphism. -/ + iso : source.toScheme ≅ target.toScheme + +namespace PartialIso + +variable {X Y Z S : Scheme.{u}} {sX : X ⟶ S} {sY : Y ⟶ S} {sZ : Z ⟶ S} + +variable (sX sY) in +/-- A partial iso is an `S`-map if the underlying morphism is. -/ +abbrev IsOver (f : X.PartialIso Y) : Prop := + f.iso.hom ≫ f.target.ι ≫ sY = f.source.ι ≫ sX + +lemma ext_iff (f g : X.PartialIso Y) : + f = g ↔ ∃ (e : f.source = g.source) (e' : g.target = f.target), + f.iso = X.isoOfEq e ≪≫ g.iso ≪≫ Y.isoOfEq e' := by + constructor + · rintro rfl + simp + · obtain ⟨U₁, hU₁, U₂, hU₂, f⟩ := f + obtain ⟨V₁, hV₁, V₂, hU₂, g⟩ := g + simp only [forall_exists_index] + rintro rfl rfl e + simpa using e + +@[ext] +lemma ext (f g : X.PartialIso Y) (e : f.source = g.source) (e' : g.target = f.target) + (H : f.iso = X.isoOfEq e ≪≫ g.iso ≪≫ Y.isoOfEq e') : f = g := by + rw [ext_iff] + exact ⟨e, e', H⟩ + +variable (X) in +/-- The identity partial isomorphism on `X`, defined on all of `X`. -/ +@[refl, simps] +def refl : X.PartialIso X where + source := ⊤ + dense_source := dense_univ + target := ⊤ + dense_target := dense_univ + iso := Iso.refl _ + +/-- The inverse of a partial isomorphism. -/ +@[symm, simps] +def symm (f : X.PartialIso Y) : Y.PartialIso X where + source := f.target + dense_source := f.dense_target + target := f.source + dense_target := f.dense_source + iso := f.iso.symm + +set_option backward.defeqAttrib.useBackward true in +lemma IsOver.symm {f : X.PartialIso Y} (hf : f.IsOver sX sY) : f.symm.IsOver sY sX := by + simpa [IsOver, ← cancel_epi f.iso.hom] using Eq.symm hf + +/-- Compose two partial isomorphisms along a proof that the target of `f` equals the source +of `g`. See `trans` for the version that does not require this. -/ +@[simps] +noncomputable def trans' (f : X.PartialIso Y) (g : Y.PartialIso Z) (e : f.target = g.source) : + X.PartialIso Z where + source := f.source + dense_source := f.dense_source + target := g.target + dense_target := g.dense_target + iso := f.iso ≪≫ Y.isoOfEq e ≪≫ g.iso + +set_option backward.defeqAttrib.useBackward true in +lemma IsOver.trans' {f : X.PartialIso Y} {g : Y.PartialIso Z} {e : f.target = g.source} + (hf : f.IsOver sX sY) (hg : g.IsOver sY sZ) : (trans' f g e).IsOver sX sZ := by + simp [IsOver, ← hf, hg] + +/-- Restrict the source of a partial isomorphism to a smaller dense open. -/ +@[simps] +noncomputable def restrictSource (f : X.PartialIso Y) (U : Opens X) (hU : Dense (U : Set X)) + (hU' : U ≤ f.source) : X.PartialIso Y where + source := U + dense_source := hU + target := f.target.ι ''ᵁ f.iso.hom ''ᵁ f.source.ι ⁻¹ᵁ U + dense_target := + have := Opens.isDominant_ι f.dense_target + f.target.ι.denseRange.dense_image f.target.ι.continuous <| + f.iso.hom.denseRange.dense_image f.iso.hom.continuous <| + hU.preimage f.source.ι.isOpenEmbedding.isOpenMap + iso := (Opens.isoOfLE hU').symm ≪≫ + (f.iso.hom.isoImage (f.source.ι ⁻¹ᵁ U)) ≪≫ + (f.target.ι.isoImage (f.iso.hom ''ᵁ f.source.ι ⁻¹ᵁ U)) + +set_option backward.defeqAttrib.useBackward true in +lemma IsOver.restrictSource {f : X.PartialIso Y} (hf : f.IsOver sX sY) (U : Opens X) + (hU : Dense (U : Set X)) (hU' : U ≤ f.source) : + (f.restrictSource U hU hU').IsOver sX sY := by + simp [IsOver, hf] + +/-- Restrict the target of a partial isomorphism to a smaller dense open. -/ +@[simps! source target iso] +noncomputable def restrictTarget (f : X.PartialIso Y) (U : Opens Y) (hU : Dense (U : Set Y)) + (hU' : U ≤ f.target) : X.PartialIso Y := + (f.symm.restrictSource U hU hU').symm + +lemma IsOver.restrictTarget {f : X.PartialIso Y} (hf : f.IsOver sX sY) (U : Opens Y) + (hU : Dense (U : Set Y)) (hU' : U ≤ f.target) : + (f.restrictTarget U hU hU').IsOver sX sY := + (hf.symm.restrictSource U hU hU').symm + +/-- Compose two partial isomorphisms, restricting to the intersection of the intermediate opens. -/ +@[trans, simps! source target iso] +noncomputable def trans (f : X.PartialIso Y) (g : Y.PartialIso Z) : X.PartialIso Z := + have := f.dense_target.inter_of_isOpen_right g.dense_source g.source.2 + (f.restrictTarget _ this inf_le_left).trans' (g.restrictSource _ this inf_le_right) rfl + +lemma IsOver.trans {f : X.PartialIso Y} {g : Y.PartialIso Z} (hf : f.IsOver sX sY) + (hg : g.IsOver sY sZ) : (f.trans g).IsOver sX sZ := + (hf.restrictTarget _ _ _).trans' (hg.restrictSource _ _ _) + +/-- The underlying partial map of a partial isomorphism. -/ +@[simps] +def toPartialMap (f : X.PartialIso Y) : X.PartialMap Y where + domain := f.source + dense_domain := f.dense_source + hom := f.iso.hom ≫ f.target.ι + +/-- The underlying rational map of a partial isomorphism. -/ +abbrev toRationalMap (f : X.PartialIso Y) : X ⤏ Y := f.toPartialMap.toRationalMap + +/-- A scheme isomorphism viewed as a partial isomorphism defined on all of `X` and `Y`. -/ +@[simps] +noncomputable def ofIso (f : X ≅ Y) : X.PartialIso Y where + source := ⊤ + dense_source := dense_univ + target := ⊤ + dense_target := dense_univ + iso := X.topIso ≪≫ f ≪≫ Y.topIso.symm + +end PartialIso + +/-- `X` and `Y` are birational if there exists a partial isomorphism between them. -/ +@[stacks 0A20 "(1)"] +def Birational (X Y : Scheme.{u}) : Prop := Nonempty (PartialIso X Y) + +/-- Choose a partial isomorphism witnessing that `X` and `Y` are birational. -/ +noncomputable def Birational.partialIso {X Y : Scheme.{u}} (h : Birational X Y) : + PartialIso X Y := + Classical.choice h + +@[refl] +lemma Birational.refl (X : Scheme.{u}) : Birational X X := + ⟨.refl X⟩ + +@[symm] +lemma Birational.symm {X Y : Scheme.{u}} (h : Birational X Y) : Birational Y X := + ⟨h.partialIso.symm⟩ + +@[trans] +lemma Birational.trans {X Y Z : Scheme.{u}} (h₁ : Birational X Y) (h₂ : Birational Y Z) : + Birational X Z := + ⟨h₁.partialIso.trans h₂.partialIso⟩ + +/-- `X` and `Y` are birational over `S` if there exists a partial isomorphism between them +that is compatible with the structure maps to `S`. -/ +def BirationalOver {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S) : Prop := + ∃ f : PartialIso X Y, f.IsOver sX sY + +/-- Choose a partial isomorphism witnessing that `X` and `Y` are birational over `S`. -/ +noncomputable def BirationalOver.partialIso {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S) + (h : BirationalOver sX sY) := + h.choose + +lemma BirationalOver.partialIso_isOver {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S) + (h : BirationalOver sX sY) : h.partialIso.IsOver sX sY := + h.choose_spec + +set_option backward.defeqAttrib.useBackward true in +lemma BirationalOver.refl {S X : Scheme.{u}} (sX : X ⟶ S) : BirationalOver sX sX := + ⟨.refl X, by simp [PartialIso.IsOver]⟩ + +lemma BirationalOver.symm {S X Y : Scheme.{u}} {sX : X ⟶ S} {sY : Y ⟶ S} + (h : BirationalOver sX sY) : BirationalOver sY sX := + ⟨h.partialIso.symm, h.partialIso_isOver.symm⟩ + +lemma BirationalOver.trans {S X Y Z : Scheme.{u}} {sX : X ⟶ S} {sY : Y ⟶ S} {sZ : Z ⟶ S} + (h₁ : BirationalOver sX sY) (h₂ : BirationalOver sY sZ) : + BirationalOver sX sZ := + ⟨h₁.partialIso.trans h₂.partialIso, h₁.partialIso_isOver.trans h₂.partialIso_isOver⟩ + +/-- `X` is rational over `S` (or `S`-rational) if it is birational over `S` to some +affine space `𝔸(n; S)`. Note that we do not require `n` to be finite here. -/ +@[mk_iff] +class IsRationalOver {S X : Scheme.{u}} (sX : X ⟶ S) : Prop where + exists_birationalOver_affineSpace (sX) : ∃ (n : Type u), BirationalOver sX (𝔸(n; S) ↘ S) + +instance (S : Scheme.{u}) (n : Type u) : IsRationalOver (𝔸(n; S) ↘ S) where + exists_birationalOver_affineSpace := ⟨n, .refl _⟩ + +/-- If a scheme `X` is `S`-birational to an `S`-rational scheme `Y`, then `X` is `S`-rational. -/ +lemma BirationalOver.isRationalOver {S X Y : Scheme.{u}} (sX : X ⟶ S) (sY : Y ⟶ S) + [IsRationalOver sY] (h : BirationalOver sX sY) : IsRationalOver sX := by + obtain ⟨n, hn⟩ := IsRationalOver.exists_birationalOver_affineSpace sY + exact ⟨n, h.trans hn⟩ + +section DenseOpen + +variable {X S : Scheme.{u}} (U : Opens X) (sX : X ⟶ S) + +/-- A dense open set `U : Opens X` induces a partial isomorphism between `U` and `X`. -/ +@[simps] +def Opens.partialIsoOfDense (hU : Dense (U : Set X)) : PartialIso U X where + source := ⊤ + dense_source := dense_univ + target := U + dense_target := hU + iso := U.toScheme.topIso + +/-- A dense open set `U : Opens X` is birational to `X`. -/ +lemma Opens.birational_of_dense (hU : Dense (U : Set X)) : Birational U X := + ⟨U.partialIsoOfDense hU⟩ + +set_option backward.defeqAttrib.useBackward true in +/-- A dense open set `U : Opens X` of a scheme `X` over `S` is `S`-birational to `X`. -/ +lemma Opens.birationalOver_of_dense (hU : Dense (U : Set X)) : BirationalOver (U.ι ≫ sX) sX := + ⟨U.partialIsoOfDense hU, by simp [PartialIso.IsOver]⟩ + +/-- A dense open set `U : Opens X` of a `S`-rational scheme `X` is `S`-rational. -/ +lemma Opens.isRationalOver_of_dense (hU : Dense (U : Set X)) [IsRationalOver sX] : + IsRationalOver (U.ι ≫ sX) := by + obtain ⟨n, hn⟩ := IsRationalOver.exists_birationalOver_affineSpace sX + exact ⟨n, (U.birationalOver_of_dense sX hU).trans hn⟩ + +end DenseOpen + +section OpenImmersion + +variable {X U S : Scheme.{u}} + +/-- A dominant open immersion `f : U ⟶ X` induces a partial isomorphism between `U` and `X`. -/ +@[simps! source target iso] +noncomputable def Hom.partialIso (f : U ⟶ X) [IsOpenImmersion f] [IsDominant f] : U.PartialIso X := + (PartialIso.ofIso f.isoOpensRange).trans' (f.opensRange.partialIsoOfDense f.denseRange) rfl + +lemma Hom.birational (f : U ⟶ X) [IsOpenImmersion f] [IsDominant f] : Birational U X := + ⟨f.partialIso⟩ + +set_option backward.defeqAttrib.useBackward true in +lemma Hom.birationalOver (f : U ⟶ X) [IsOpenImmersion f] [IsDominant f] (sX : X ⟶ S) (sU : U ⟶ S) + (hf : f ≫ sX = sU) : BirationalOver sU sX := + ⟨f.partialIso, by simp [PartialIso.IsOver, hf]⟩ + +end OpenImmersion + +end AlgebraicGeometry.Scheme diff --git a/Mathlib/AlgebraicGeometry/Cover/Open.lean b/Mathlib/AlgebraicGeometry/Cover/Open.lean index 95e6a4c5d07fb0..7d4cf90cf18dc8 100644 --- a/Mathlib/AlgebraicGeometry/Cover/Open.lean +++ b/Mathlib/AlgebraicGeometry/Cover/Open.lean @@ -44,6 +44,10 @@ variable [∀ x, HasPullback (𝒰.f x ≫ f) g] instance (i : 𝒰.I₀) : IsOpenImmersion (𝒰.f i) := 𝒰.map_prop i +instance {𝒱 : OpenCover X} (f : 𝒰 ⟶ 𝒱) (i : 𝒰.I₀) : IsOpenImmersion (f.h₀ i) := + have : IsOpenImmersion (f.h₀ i ≫ 𝒱.f (f.s₀ i)) := by rw [f.w₀]; infer_instance + .of_comp _ (𝒱.f _) + set_option backward.isDefEq.respectTransparency false in /-- The affine cover of a scheme. -/ def affineCover (X : Scheme.{u}) : OpenCover X := by diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean index e827a3073c3ef7..a0d9e31f7c3413 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/AddSubMap.lean @@ -5,8 +5,7 @@ Authors: Michael Stoll -/ module -public import Mathlib.AlgebraicGeometry.EllipticCurve.Weierstrass -public import Mathlib.LinearAlgebra.Matrix.Notation +public import Mathlib.AlgebraicGeometry.EllipticCurve.Affine.Point public import Mathlib.RingTheory.MvPolynomial.Homogeneous public import Mathlib.Tactic.Ring.NamePolyVars @@ -129,4 +128,58 @@ lemma addSubMap_ne_zero [IsReduced R] {x : Fin 3 → R} (hx : x ≠ 0) : end WeierstrassCurve +/-! +### The symmetric square of the x-coordinate map + +We define `Weierstrass.Affine.Point.sym2x`, which sends a pair `P`, `Q` of nonsingular points in +affine coordinates on a Weierstrass curve to a triple projectively equal to +`(x(P)*x(Q), x(P)+x(Q), 1)`, and provide some API. +-/ + +namespace WeierstrassCurve.Affine.Point + +variable {R : Type*} [CommRing R] {W' : Affine R} + +/-- This map sends a pair `P`, `Q` of nonsingular points in affine coordinates on `W` +to a triple projectively equivalent to `![x(P) * x(Q), x(P) + x(Q), 1]`. + +In more geometric terms, this is the map `Sym² W → Sym² ℙ¹ ≃ ℙ²` induced by `x : W → ℙ¹`. -/ +noncomputable def sym2x (P Q : W'.Point) : Fin 3 → R := + letI Px := P.xRep + letI Qx := Q.xRep + ![Px 0 * Qx 0, Px 0 * Qx 1 + Px 1 * Qx 0, Px 1 * Qx 1] + +@[simp] +lemma sym2x_zero_zero : (0 : W'.Point).sym2x 0 = ![1, 0, 0] := by + simp [sym2x] + +@[simp] +lemma sym2x_zero_some {x y : R} (h : W'.Nonsingular x y) : + (0 : W'.Point).sym2x (some x y h) = ![x, 1, 0] := by + simp [sym2x] + +@[simp] +lemma sym2x_some_zero {x y : R} (h : W'.Nonsingular x y) : + (some x y h).sym2x 0 = ![x, 1, 0] := by + simp [sym2x] + +@[simp] +lemma sym2x_some_some {x y x' y' : R} (h : W'.Nonsingular x y) (h' : W'.Nonsingular x' y') : + (some x y h).sym2x (some x' y' h') = ![x * x', x + x', 1] := by + simp [sym2x] + +lemma sym2x_ne_zero [Nontrivial R] (P Q : W'.Point) : P.sym2x Q ≠ 0 := by + cases P <;> cases Q <;> simp [sym2x, xRep] + +lemma sym2x_comm (P Q : W'.Point) : P.sym2x Q = Q.sym2x P := by + cases P <;> cases Q <;> simp [← zero_def, mul_comm, add_comm] + +lemma sym2x_neg_left (P Q : W'.Point) : (-P).sym2x Q = P.sym2x Q := by + simp [sym2x] + +lemma sym2x_neg_right (P Q : W'.Point) : P.sym2x (-Q) = P.sym2x Q := by + simp [sym2x] + +end WeierstrassCurve.Affine.Point + end diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean index 942de0e5a8d33f..6adf1fe57679c8 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Formula.lean @@ -365,15 +365,13 @@ lemma addX_eq_addX_negY_sub {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) -- Non-terminal simp, used to be field_simp set_option linter.flexible false in --- see https://github.com/leanprover-community/mathlib4/issues/29041 -set_option linter.unusedSimpArgs false in /-- The formula `y(P₁)(x(P₂) - x(P₃)) + y(P₂)(x(P₃) - x(P₁)) + y(P₃)(x(P₁) - x(P₂)) = 0`, assuming that `P₁ + P₂ + P₃ = O`. -/ lemma cyclic_sum_Y_mul_X_sub_X {x₁ x₂ : F} (y₁ y₂ : F) (hx : x₁ ≠ x₂) : let x₃ := W.addX x₁ x₂ (W.slope x₁ x₂ y₁ y₂) y₁ * (x₂ - x₃) + y₂ * (x₃ - x₁) + W.negAddY x₁ x₂ y₁ (W.slope x₁ x₂ y₁ y₂) * (x₁ - x₂) = 0 := by simp_rw [slope_of_X_ne hx, negAddY, addX] - simp [field, sub_ne_zero.mpr hx] + simp [field] ring1 /-- The formula `ψ(P₁ + P₂) = (ψ(P₂)(x(P₁) - x(P₃)) - ψ(P₁)(x(P₂) - x(P₃))) / (x(P₂) - x(P₁))`, diff --git a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean index 671eb7272d0753..5e2d6d1ff2f2c2 100644 --- a/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean +++ b/Mathlib/AlgebraicGeometry/EllipticCurve/Affine/Point.lean @@ -271,8 +271,6 @@ lemma XYIdeal_eq₁ (x y ℓ : R) : XYIdeal W' x (C y) = XYIdeal W' x (linePolyn -- Non-terminal simp, used to be field_simp set_option linter.flexible false in --- see https://github.com/leanprover-community/mathlib4/issues/29041 -set_option linter.unusedSimpArgs false in lemma XYIdeal_eq₂ [DecidableEq F] {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation x₁ y₁) (h₂ : W.Equation x₂ y₂) (hxy : ¬(x₁ = x₂ ∧ y₁ = W.negY x₂ y₂)) : XYIdeal W x₂ (C y₂) = XYIdeal W x₂ (linePolynomial x₁ y₁ <| W.slope x₁ x₂ y₁ y₂) := by @@ -281,7 +279,7 @@ lemma XYIdeal_eq₂ [DecidableEq F] {x₁ x₂ y₁ y₂ : F} (h₁ : W.Equation · have hy : y₁ ≠ W.negY x₂ y₂ := fun h => hxy ⟨hx, h⟩ rcases hx, Y_eq_of_Y_ne h₁ h₂ hx hy with ⟨rfl, rfl⟩ simp [linePolynomial] - · simp [field, linePolynomial, slope_of_X_ne hx, sub_ne_zero_of_ne hx] + · simp [field, linePolynomial, slope_of_X_ne hx] ring1 nth_rw 1 [hy₂] simp only [XYIdeal, XClass, YClass, linePolynomial] diff --git a/Mathlib/AlgebraicGeometry/FunctionField.lean b/Mathlib/AlgebraicGeometry/FunctionField.lean index 04b8ec01e7439f..0c18e17f26aaf6 100644 --- a/Mathlib/AlgebraicGeometry/FunctionField.lean +++ b/Mathlib/AlgebraicGeometry/FunctionField.lean @@ -91,6 +91,13 @@ instance functionField_isScalarTower [IrreducibleSpace X] (U : X.Opens) (x : U) change _ = (X.presheaf.germ U x x.2 ≫ _).hom rw [X.presheaf.germ_stalkSpecializes] +@[simp] +lemma Scheme.algebraMap_germ_eq_germToFunctionField [IrreducibleSpace X] + {U : X.Opens} [Nonempty U] {x : X} (hx : x ∈ U) (f : Γ(X, U)) : + algebraMap (X.presheaf.stalk x) X.functionField (X.presheaf.germ U x hx f) = + X.germToFunctionField U f := by + simp [RingHom.algebraMap_toAlgebra, ← ConcreteCategory.comp_apply] + noncomputable instance (R : CommRingCat.{u}) [IsDomain R] : Algebra R (Spec R).functionField := -- TODO: can we write this normally after the refactor finishes? @@ -171,4 +178,27 @@ instance [IsIntegral X] (x : X) : instance [IsIntegral X] {x : X} : IsDomain (X.presheaf.stalk x) := Function.Injective.isDomain _ (IsFractionRing.injective (X.presheaf.stalk x) (X.functionField)) +/-- +For `f` an element of the function field of `X`, there exists some open set `U ⊆ X` such that +`f` is a unit in `Γ(X, U)`. +-/ +lemma exists_isUnit_germ_eq [IsIntegral X] (f : X.functionField) (hf : f ≠ 0) : + ∃ U ∈ X.affineOpens, ∃ f' : Γ(X, U), ∃ _ : Nonempty U, + X.germToFunctionField U f' = f ∧ IsUnit f' := by + obtain ⟨U, hU, g, hg⟩ := X.presheaf.exists_germ_eq f + obtain ⟨_, ⟨A, hA, rfl⟩, hxA, hAU⟩ := + X.isBasis_affineOpens.exists_subset_of_mem_open hU U.isOpen + have : Nonempty A := ⟨_, hxA⟩ + let gA : Γ(X, A) := X.presheaf.map (homOfLE hAU).op g + have h_germ_gA : X.presheaf.germ A (genericPoint X) hxA gA = f := by + simp only [← hg, ← X.presheaf.germ_res_apply (homOfLE hAU) (genericPoint X) hxA g, gA] + rfl + have hxV : genericPoint X ∈ X.basicOpen gA := by + rwa [Scheme.mem_basicOpen X gA (genericPoint X) hxA, h_germ_gA, isUnit_iff_ne_zero] + have : Nonempty (X.basicOpen gA) := ⟨⟨_, hxV⟩⟩ + refine ⟨X.basicOpen gA, hA.basicOpen gA, + X.presheaf.map (X.basicOpen_le gA).hom.op gA, ‹_›, ?_, + X.toRingedSpace.isUnit_res_basicOpen gA⟩ + simpa using h_germ_gA + end AlgebraicGeometry diff --git a/Mathlib/AlgebraicGeometry/Limits.lean b/Mathlib/AlgebraicGeometry/Limits.lean index a832ac5e0c047b..d2058253707e20 100644 --- a/Mathlib/AlgebraicGeometry/Limits.lean +++ b/Mathlib/AlgebraicGeometry/Limits.lean @@ -442,7 +442,7 @@ lemma isPullback_inl_inl_coprodMap {X Y X' Y' : Scheme.{u}} · simp only [coprodMk_inr, ← Scheme.Hom.comp_apply, coprod.inr_map] at hxy cases Set.disjoint_iff_forall_ne.mp (isCompl_range_inl_inr _ _).1 ⟨y, rfl⟩ ⟨_, rfl⟩ hxy · rintro _ ⟨x, rfl⟩ - exact ⟨f x, by simp [← Scheme.Hom.comp_apply, - Scheme.Hom.comp_base]⟩ + exact ⟨f x, by simp [← Scheme.Hom.comp_apply, -Scheme.Hom.comp_base]⟩ set_option backward.isDefEq.respectTransparency false in lemma isPullback_inr_inr_coprodMap {X Y X' Y' : Scheme.{u}} diff --git a/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean b/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean index b415ed001bbaa5..32ec8d0dc0624f 100644 --- a/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean +++ b/Mathlib/AlgebraicGeometry/Modules/Sheaf.lean @@ -10,9 +10,11 @@ public import Mathlib.Algebra.Category.ModuleCat.Sheaf.Colimits public import Mathlib.Algebra.Category.ModuleCat.Sheaf.PullbackContinuous public import Mathlib.AlgebraicGeometry.Modules.Presheaf public import Mathlib.AlgebraicGeometry.OpenImmersion +public import Mathlib.AlgebraicGeometry.AffineScheme public import Mathlib.CategoryTheory.Bicategory.Adjunction.Adj public import Mathlib.CategoryTheory.Bicategory.Adjunction.Cat public import Mathlib.CategoryTheory.Bicategory.Functor.LocallyDiscrete +public import Mathlib.Topology.Sheaves.Module /-! # The category of sheaves of modules over a scheme @@ -381,6 +383,23 @@ instead. -/ lemma restrict_map (M : Y.Modules) (f : X ⟶ Y) [IsOpenImmersion f] {U V} (i : U ⟶ V) : (M.restrict f).presheaf.map i.op = M.presheaf.map (f.opensFunctor.map i).op := rfl +/-- `Scheme.Modules.restrict` along an open immersion `X ⟶ Y` sends `𝒪_Y` to `𝒪_X`. -/ +def restrictUnitIso (f : X ⟶ Y) [IsOpenImmersion f] : + restrict (.unit <| Y.ringCatSheaf) f ≅ .unit X.ringCatSheaf := by + refine (fullyFaithfulForget _).preimageIso <| PresheafOfModules.isoMk (fun U ↦ ?_) ?_ + · refine ModuleCat.isoMk + ((forget₂ CommRingCat RingCat ⋙ forget₂ _ Ab).mapIso (f.appIso U.unop)) ?_ + intro (r : Γ(X, U.unop)) + ext (x : Γ(Y, f ''ᵁ U.unop)) + change r * (f.appIso U.unop).hom x = (f.appIso U.unop).hom ((f.appIso U.unop).inv r * x) + simp + · intro U V g + have : Y.presheaf.map (homOfLE (by grw [leOfHom g.unop])).op ≫ + (f.appIso _).hom = (f.appIso U.unop).hom ≫ X.presheaf.map g := by + simp [Hom.appIso_hom'] + ext x + exact congr($(this) x) + /-- The restriction of a module along an open immersion. -/ def restrictFunctorAdjCounitIso : pushforward f ⋙ restrictFunctor f ≅ 𝟭 _ := letI := CategoryTheory.Functor.isContinuous_comp.{u} f.opensFunctor (Opens.map f.base) @@ -521,4 +540,62 @@ noncomputable def sheafComposePushforwardComp {R S : CommRingCat.{u}} (φ : R · cat_disch · cat_disch +/-- Sheaves of modules on `𝒪_X` restricted to `U` are equivalent to sheaves of `𝒪_U`-modules. -/ +noncomputable +def overEquiv {X : Scheme.{u}} (U : X.Opens) : + SheafOfModules (X.ringCatSheaf.over U) ≌ (U : Scheme.{u}).Modules := + TopologicalSpace.Opens.sheafOfModulesEquivOver _ _ + +set_option backward.isDefEq.respectTransparency false in +/-- Up to `Scheme.Modules.overEquiv`, `SheafOfModules.overMap` is isomorphic to +`Scheme.Modules.restrictFunctor`. -/ +noncomputable +def overMapCompOverEquiv {X : Scheme.{u}} {U V : X.Opens} (f : V ⟶ U) : + overMap X.ringCatSheaf f ⋙ (overEquiv V).functor ≅ + (overEquiv U).functor ⋙ restrictFunctor (X.homOfLE <| leOfHom f) := by + haveI : (Hom.opensFunctor (X.homOfLE <| leOfHom f)).IsContinuous + (Opens.grothendieckTopology V.toScheme) (Opens.grothendieckTopology U.carrier) := + inferInstanceAs <| + (Hom.opensFunctor (X.homOfLE <| leOfHom f)).IsContinuous _ + (Opens.grothendieckTopology U.toScheme) + haveI := U.instIsDenseSubsiteSubtypeMemOverGrothendieckTopologyOverInverseOverEquivalence + haveI : (Hom.opensFunctor (X.homOfLE <| leOfHom f)).IsContinuous + (Opens.grothendieckTopology ↥V) (Opens.grothendieckTopology U.toScheme) := + inferInstanceAs <| (X.homOfLE <| leOfHom f).opensFunctor.IsContinuous + (Opens.grothendieckTopology V.toScheme) (Opens.grothendieckTopology U.toScheme) + haveI : ((Opens.overEquivalence V).symm.functor ⋙ Over.map f).IsContinuous + (Opens.grothendieckTopology ↥V) ((Opens.grothendieckTopology X).over U) := + Functor.isContinuous_comp _ _ _ (.over (Opens.grothendieckTopology _) _) _ + haveI : (Opens.overEquivalence U).symm.functor.IsContinuous (Opens.grothendieckTopology U) + ((Opens.grothendieckTopology X).over U) := + inferInstanceAs <| U.overEquivalence.inverse.IsContinuous (Opens.grothendieckTopology U.carrier) + ((Opens.grothendieckTopology X).over U) + haveI : ((X.homOfLE (leOfHom f)).opensFunctor ⋙ + (Opens.overEquivalence U).symm.functor).IsContinuous (Opens.grothendieckTopology ↥V) + ((Opens.grothendieckTopology ↥X).over U) := + Functor.isContinuous_comp _ _ _ (Opens.grothendieckTopology _) _ + refine (SheafOfModules.pushforwardComp _ _) ≪≫ ?_ ≪≫ (SheafOfModules.pushforwardComp _ _).symm + refine SheafOfModules.pushforwardCongr₂ _ ?_ ?_ + · refine NatIso.ofComponents (fun W ↦ Over.isoMk (eqToIso ?_) ?_) ?_ + · suffices U.ι ''ᵁ ((X.homOfLE (leOfHom f)) ''ᵁ W) = V.ι ''ᵁ W by simpa + simp [← Scheme.Hom.comp_image] + · cat_disch + · cat_disch + · ext W x + suffices X.presheaf.map _ x = ((X.homOfLE <| leOfHom f).appIso _).inv x by simpa + rw [Scheme.Hom.appIso_homOfLE_inv] + rfl + +/-- Up to `Scheme.Modules.overEquiv`, `SheafOfModules.overFunctor` is isomorphic to +`Scheme.Modules.restrictFunctor`. -/ +noncomputable +def overFunctorEquiv {X : Scheme.{u}} (U : X.Opens) : + overFunctor X.ringCatSheaf U ⋙ (overEquiv U).functor ≅ restrictFunctor U.ι := by + have : ((Opens.overEquivalence U).symm.functor ⋙ Over.forget U).IsContinuous + (Opens.grothendieckTopology ↥U) (Opens.grothendieckTopology ↥X) := + Functor.isContinuous_comp _ _ _ (.over (Opens.grothendieckTopology _) U) _ + refine SheafOfModules.pushforwardComp _ _ ≪≫ SheafOfModules.pushforwardCongr ?_ + simp only [CategoryTheory.Functor.map_id, Opposite.op_unop, Opens.ι_appIso, Iso.refl_inv] + rfl + end AlgebraicGeometry.Scheme.Modules diff --git a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean index 92eacec3e0c9e2..d32e5611a85a5b 100644 --- a/Mathlib/AlgebraicGeometry/Modules/Tilde.lean +++ b/Mathlib/AlgebraicGeometry/Modules/Tilde.lean @@ -8,8 +8,9 @@ module public import Mathlib.Algebra.Category.ModuleCat.Localization public import Mathlib.Algebra.Category.ModuleCat.Sheaf.Quasicoherent -public import Mathlib.AlgebraicGeometry.AffineScheme +public import Mathlib.Algebra.Module.LocalizedModule.Away public import Mathlib.AlgebraicGeometry.Modules.Sheaf +public import Mathlib.Data.Fintype.Order /-! @@ -450,6 +451,9 @@ def presentationTilde (s : Set M) (hs : Submodule.span R s = ⊤) instance : (tilde M).IsQuasicoherent := (presentationTilde.{u} _ .univ (by simp) _ (Submodule.span_eq _)).isQuasicoherent +instance : ((tilde.functor R).obj M).IsQuasicoherent := + inferInstanceAs <| (tilde M).IsQuasicoherent + set_option backward.isDefEq.respectTransparency false in lemma isIso_fromTildeΓ_of_presentation (M : (Spec R).Modules) (P : M.Presentation) : IsIso M.fromTildeΓ := by @@ -553,6 +557,343 @@ theorem isIso_fromTildeΓ_pushforward (M : (Spec S).Modules) [h : IsIso M.fromTi end IsLocalizing +set_option backward.isDefEq.respectTransparency false in +instance Scheme.Modules.isQuasicoherent_restrictFunctor {X Y : Scheme.{u}} (f : X ⟶ Y) + [IsOpenImmersion f] (M : Y.Modules) [M.IsQuasicoherent] : + ((restrictFunctor f).obj M).IsQuasicoherent := by + let α : X.presheaf ⟶ f.opensFunctor.op ⋙ Y.presheaf := { app U := (f.appIso U.unop).inv } + have hα : IsIso α := NatIso.isIso_of_isIso_app _ + let φ : X.ringCatSheaf ⟶ (f.opensFunctor.sheafPushforwardContinuous _ _ _).obj Y.ringCatSheaf := + ⟨Functor.whiskerRight α (forget₂ CommRingCat RingCat)⟩ + have : IsIso φ := by + rw [← isIso_iff_of_reflects_iso _ (ObjectProperty.ι _)] + dsimp [φ] + infer_instance + exact SheafOfModules.isQuasicoherent_pushforward_of_isLeftAdjoint.{u} + f.opensFunctor φ (Scheme.Modules.restrictUnitIso _) + +set_option backward.isDefEq.respectTransparency false in +/-- The presentation of `M.restrict f` by restricting a presentation of `M`. -/ +def Scheme.Modules.presentationRestrict {X Y : Scheme.{u}} (f : Y ⟶ X) + [IsOpenImmersion f] {M : X.Modules} (pres : M.Presentation) : + (M.restrict f).Presentation := + have : PreservesColimitsOfSize.{u, u} (Scheme.Modules.restrictFunctor f) := + inferInstance + pres.map (Scheme.Modules.restrictFunctor.{u} f) (Scheme.Modules.restrictUnitIso _).symm + +set_option backward.isDefEq.respectTransparency false in +lemma Scheme.Modules.exists_isOpenCover_presentation {X : Scheme.{u}} (M : X.Modules) + [M.IsQuasicoherent] : + ∃ (ι : Type u) (U : ι → X.Opens) (_ : ∀ i, (M.restrict (U i).ι).Presentation), + IsOpenCover U ∧ (∀ i, IsAffineOpen (U i)) := by + obtain ⟨⟨I, W, cov, pres⟩⟩ := SheafOfModules.IsQuasicoherent.nonempty_quasicoherentData (M := M) + choose κ hsub heq using fun i ↦ Opens.isBasis_iff_cover.mp X.isBasis_affineOpens (W i) + refine ⟨Σ (i : I), κ i, fun j ↦ j.2, fun i ↦ ?_, ?_, ?_⟩ + · let u := X.homOfLE (U := i.2) (V := W i.1) (by simp [heq, le_sSup]) + have : PreservesColimitsOfSize.{u, u} (restrictFunctor u) := inferInstance + let F := (overEquiv (W i.1)).functor ⋙ restrictFunctor u + let iso : SheafOfModules.overFunctor X.ringCatSheaf _ ⋙ F ≅ restrictFunctor + (Scheme.Opens.ι i.2.1) := (Functor.associator _ _ _).symm ≪≫ + Functor.isoWhiskerRight (Scheme.Modules.overFunctorEquiv _) _ ≪≫ + (restrictFunctorComp _ _).symm ≪≫ (restrictFunctorCongr (by simp [u])) + exact SheafOfModules.Presentation.ofIsIso.{u, u, u} (iso.app M).hom <| + (pres i.1).map F (Scheme.Modules.restrictUnitIso _).symm + · rw [Opens.coversTop_iff, IsOpenCover] at cov + rw [IsOpenCover, iSup_sigma, ← cov] + refine iSup_congr fun i ↦ ?_ + rw [heq i, sSup_eq_iSup'] + · intro j + exact hsub _ j.2.2 + +lemma Scheme.Modules.exists_affineOpenCover_presentation {X : Scheme.{u}} (M : X.Modules) + [M.IsQuasicoherent] : + ∃ (𝒰 : Scheme.AffineOpenCover.{u} X), + ∀ i, Nonempty (M.restrict (𝒰.f i)).Presentation := by + obtain ⟨ι, U, pres, hU, hU'⟩ := M.exists_isOpenCover_presentation + refine ⟨Scheme.AffineOpenCover.ofIsOpenCover _ hU hU', fun i ↦ ⟨?_⟩⟩ + exact SheafOfModules.Presentation.ofIsIso.{u, u, u} ((restrictFunctorComp _ _).app M).inv <| + (presentationRestrict (hU' i).isoSpec.inv (pres i)) + +namespace QuasicoherentTilde + +variable (M : (Spec R).Modules) + +/-- Auxiliary structure used in the proof of `Scheme.Modules.isIso_fromTildeΓ_of_isQuasicoherent`. +These are conditions d1) and d2) from [Theoreme 1.4.1, grothendieck-1971]. -/ +-- TODO: Generalise this to a general scheme, replacing `f : R` by sections over a suitable set. +private structure Aux (V : (Spec R).Opens) where + existence (f : R) (hf : basicOpen f ≤ V) (s : Γ(M, basicOpen f)) : + ∃ (n : ℕ) (t : Γ(M, V)), M.presheaf.map (homOfLE hf).op t = f ^ n • s + uniqueness (f : R) (hf : basicOpen f ≤ V) (t : Γ(M, V)) : + M.presheaf.map (.op <| homOfLE hf) t = (0 : Γ(M, basicOpen f)) → + ∃ (n : ℕ), f ^ n • t = 0 + +set_option backward.isDefEq.respectTransparency false in +private lemma Aux.of_le {M : (Spec R).Modules} {V : (Spec R).Opens} (g : R) (hg : basicOpen g ≤ V) + (hV : Aux M V) : + Aux M (basicOpen g) where + existence f hfg s := by + obtain ⟨n, t, ht⟩ := hV.existence f (le_trans hfg hg) s + use n, M.presheaf.map (homOfLE hg).op t + simp [← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp, ht] + uniqueness f hfg t ht := by + obtain ⟨n, t', ht'⟩ := hV.existence g hg t + obtain ⟨m, hm⟩ := hV.uniqueness _ (le_trans hfg hg) t' <| by + rw [← homOfLE_comp hfg hg, op_comp, M.presheaf.map_comp_apply, ht', M.map_smul_Spec, ht] + simp + refine ⟨m, ((M.isSMulRegular_of_le_basicOpen le_rfl).pow n).right_eq_zero_of_smul ?_⟩ + simp [smul_comm, ← ht', ← M.map_smul_Spec, hm] + +set_option backward.isDefEq.respectTransparency false in +/-- This is the key computation for the proof of +`Scheme.Modules.isQuasicoherent_iff_isIso_fromTildeΓ`. + [Lemme 1.4.1.1][grothendieck-1971] -/ +private lemma Aux.of_eq_iSup_basicOpen {M : (Spec R).Modules} (V : (Spec R).Opens) + {ι : Type*} [Finite ι] (g : ι → R) (hg : V = ⨆ i, basicOpen (g i)) + (h₁ : ∀ (i : ι), Aux M (basicOpen (g i))) : + Aux M V := by + have h₂ (i j : ι) : Aux M (basicOpen (g i * g j)) := + .of_le _ (basicOpen_mul_le_left _ _) (h₁ i) + have hgle (i : ι) : basicOpen (g i) ≤ V := by rw [hg]; exact le_iSup_of_le _ le_rfl + have hug (i : ι) (m : ℕ) : + IsUnit (algebraMap R (Module.End R Γ(M, basicOpen (g i))) (g i ^ m)) := by + rw [map_pow] + exact (Scheme.Modules.isUnit_algebraMap_end_of_le_basicOpen (g i) le_rfl).pow m + -- We show existence and uniqueness separately. + refine ⟨fun f hf s ↦ ?_, fun f hf t hs ↦ ?_⟩ + · have hfgi (i : ι) : basicOpen (f * g i) ≤ basicOpen (g i) := basicOpen_mul_le_right f (g i) + let s' (i : ι) : Γ(M, basicOpen (f * g i)) := + M.presheaf.map (homOfLE <| basicOpen_mul_le_left f (g i)).op s + /- By `h₁`, up to a factor of `f ^ N`, the restrictions of `s` to `D(f) ∩ D(gᵢ)` lift + to sections `tᵢ` over `D(gᵢ)`. -/ + obtain ⟨N, t, ht⟩ : ∃ (N : ℕ) (t : ∀ i, Γ(M, basicOpen (g i))), + ∀ i, f ^ N • s' i = M.presheaf.map (homOfLE (basicOpen_mul_le_right f (g i))).op (t i) := by + have (i : ι) : ∃ (n : ℕ) (t : Γ(M, basicOpen (g i))), + f ^ n • s' i = M.presheaf.map (homOfLE (hfgi i)).op t := by + obtain ⟨n, t', ht'⟩ := (h₁ i).existence (f * g i) (hfgi i) (s' i) + rw [mul_pow, mul_smul, smul_comm] at ht' + obtain ⟨ψ, hψ⟩ := IsUnit.exists_right_inv (hug i n) + use n, ψ t' + apply (M.isSMulRegular_of_le_basicOpen (basicOpen_mul_le_right f (g i))).pow n + dsimp + rw [← ht', ← Scheme.Modules.map_smul_Spec] + congr 1 + exact congr($hψ t').symm + choose n t' ht' using this + have (i : ι) : n i ≤ ⨆ i, n i := le_ciSup (Finite.bddAbove_range _) _ + have hN (i : ι) : ⨆ i, n i = ((⨆ i, n i) - n i) + n i := by grind + refine ⟨⨆ i, n i, fun i ↦ f ^ ((⨆ i, n i) - n i) • t' i, fun i ↦ ?_⟩ + conv_lhs => rw [hN i] + rw [pow_add, mul_smul, ht', M.map_smul_Spec] + /- By `h₂`, up to a factor of `f ^ K`, the restrictions of `tᵢ` and `tⱼ` to + to `D(gᵢ) ∩ D(gⱼ)` agree. -/ + obtain ⟨K, hK⟩ : ∃ (K : ℕ), ∀ (i j : ι), + M.presheaf.map (homOfLE (basicOpen_mul_le_left (g i) (g j))).op (f ^ K • t i) = + M.presheaf.map (homOfLE (basicOpen_mul_le_right (g i) (g j))).op (f ^ K • t j) := by + have (i j : ι) : ∃ (m : ℕ), + M.presheaf.map (homOfLE (basicOpen_mul_le_left (g i) (g j))).op (f ^ m • t i) = + M.presheaf.map (homOfLE (basicOpen_mul_le_right (g i) (g j))).op (f ^ m • t j) := by + have := (h₂ i j).uniqueness (f * (g i * g j)) (basicOpen_mul_le_right _ _) + (M.presheaf.map (homOfLE (basicOpen_mul_le_left (g i) (g j))).op (t i) - + M.presheaf.map (homOfLE (basicOpen_mul_le_right (g i) (g j))).op (t j)) ?_ + · obtain ⟨m, hm⟩ := this + use m + apply (M.isSMulRegular_of_le_basicOpen le_rfl).pow m + simpa [M.map_smul_Spec _ (f ^ m), ← mul_smul, ← mul_smul, ← mul_pow, ← mul_comm f, + smul_sub, sub_eq_zero] using hm + · have hfgigi : basicOpen (f * (g i * g j)) ≤ basicOpen (f * g i) := by + rw [← mul_assoc] + exact basicOpen_mul_le_left _ _ + have hfgigj : basicOpen (f * (g i * g j)) ≤ basicOpen (f * g j) := by + rw [mul_comm (g i) (g j), ← mul_assoc] + exact basicOpen_mul_le_left _ _ + rw [map_sub, ← M.presheaf.map_comp_apply, ← op_comp, ← M.presheaf.map_comp_apply, + ← op_comp, homOfLE_comp, homOfLE_comp, ← homOfLE_comp hfgigi (hfgi i), + ← homOfLE_comp hfgigj (hfgi j), op_comp, M.presheaf.map_comp_apply, ← ht i, + M.map_smul_Spec, ← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp, op_comp, + M.presheaf.map_comp_apply, ← ht j, M.map_smul_Spec, ← M.presheaf.map_comp_apply, + ← op_comp, homOfLE_comp] + simp + choose m hm using this + let K := ⨆ i, ⨆ j, m i j + refine ⟨K, fun i j ↦ ?_⟩ + have : m i j ≤ K := + le_ciSup_of_le (Finite.bddAbove_range _) i (le_ciSup (Finite.bddAbove_range _) _) + have : K = (K - m i j) + m i j := by lia + rw [this, pow_add, mul_smul, mul_smul, M.map_smul_Spec, M.map_smul_Spec _ (f ^ (K - m i j)), + hm i j] + -- So up to a factor of `f ^ (N + K)`, the `tᵢ` glue. + refine ⟨N + K, ?_⟩ + have := TopCat.Sheaf.existsUnique_gluing' ⟨_, M.isSheaf⟩ (fun i ↦ basicOpen (g i)) V + (fun i ↦ homOfLE (by rw [hg]; exact le_iSup_of_le _ le_rfl)) (by simp [hg]) + (fun i ↦ f ^ K • t i) ?_ + · obtain ⟨a, ha, -⟩ := this + use a + refine TopCat.Sheaf.eq_of_locally_eq' ⟨_, M.isSheaf⟩ (fun i ↦ basicOpen (f * g i)) _ + (fun i ↦ homOfLE (basicOpen_mul_le_left f (g i))) ?_ _ _ ?_ + · rw [left_eq_inf.mpr hf, hg, inf_iSup_eq] + simp_rw [basicOpen_mul] + exact le_rfl + · intro i + rw [← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp, + ← homOfLE_comp (basicOpen_mul_le_right _ _) (hgle i), op_comp, M.presheaf.map_comp_apply, + M.map_smul_Spec, ha, M.map_smul_Spec, pow_add, mul_smul, smul_comm, ht i] + · intro i j + have : Function.Injective (M.presheaf.map (eqToHom <| (basicOpen_mul (g i) (g j))).op) := + ConcreteCategory.injective_of_mono_of_preservesPullback _ + apply this + dsimp [Opens.infLELeft, Opens.infLERight] + simp_rw [← M.presheaf.map_comp_apply, ← op_comp, eqToHom_comp_homOfLE] + exact hK i j + · have (i : ι) : ∃ (n : ℕ), M.presheaf.map (homOfLE (hgle i)).op (f ^ n • t) = 0 := by + have := (h₁ i).uniqueness (f * g i) (basicOpen_mul_le_right f (g i)) + (M.presheaf.map (homOfLE (hgle i)).op t) ?_ + · obtain ⟨n, hn⟩ := this + use n + rw [mul_pow, mul_comm, mul_smul, ← Scheme.Modules.map_smul_Spec] at hn + exact ((M.isSMulRegular_of_le_basicOpen le_rfl).pow n).right_eq_zero_of_smul hn + · rw [← M.presheaf.map_comp_apply, ← op_comp, homOfLE_comp, + ← homOfLE_comp ((basicOpen_mul_le_left f (g i))) hf, op_comp, M.presheaf.map_comp_apply] + simp [hs] + choose n hn using this + use ⨆ i, n i + apply TopCat.Sheaf.eq_of_locally_eq' ⟨_, M.isSheaf⟩ (fun i ↦ basicOpen (g i)) _ + (fun i ↦ homOfLE (by rw [hg]; exact le_iSup_of_le _ le_rfl)) + · simp [hg] + · intro i + have : n i ≤ ⨆ i, n i := le_ciSup (Finite.bddAbove_range _) _ + have : ⨆ i, n i = ((⨆ i, n i) - n i) + n i := by lia + rw [this, pow_add, mul_smul, Scheme.Modules.map_smul_Spec, hn i] + simp + +private lemma isLocalizing_iff_aux (M : (Spec R).Modules) : + IsLocalizing (modulesSpecToSheaf.obj M) ↔ Aux M ⊤ := by + let φ (f : R) := ((modulesSpecToSheaf.obj M).obj.map (basicOpen f).leTop.op).hom + refine ⟨fun h ↦ ?_, fun h f ↦ IsLocalizedModule.Away.mk_of_addCommGroup ?_ ?_ ?_⟩ + · have hf (f : R) : IsLocalizedModule.Away f (φ f) := h f + refine ⟨fun f hle s ↦ ?_, fun f hle s hs ↦ ?_⟩ + · obtain ⟨n, y, hy⟩ := (hf f).surj _ _ s + use n, y, hy.symm + · obtain ⟨⟨_, n, rfl⟩, hn⟩ := (IsLocalizedModule.eq_zero_iff (.powers f) (φ f)).mp hs + use n, hn + · exact Scheme.Modules.isUnit_algebraMap_end_of_le_basicOpen f le_rfl + · intro x + obtain ⟨n, t, ht⟩ := h.existence _ _ x + use n, t, ht.symm + · intro x hx + obtain ⟨n, hn⟩ := h.uniqueness _ _ _ hx + use n, hn + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +private lemma aux_basicOpen_of_aux_restrict (M : (Spec R).Modules) (g : R) + (h : Aux (M.restrict <| + Spec.map <| CommRingCat.ofHom <| algebraMap R <| Localization.Away g) ⊤) : + Aux M (basicOpen g) := by + let a : R ⟶ CommRingCat.of (Localization.Away g) := + CommRingCat.ofHom <| algebraMap R _ + set ψ : Spec (.of <| Localization.Away g) ⟶ Spec (.of R) := Spec.map a + set M' : (Spec (.of <| Localization.Away g)).Modules := M.restrict ψ + have heq (f : R) (hf : basicOpen f ≤ basicOpen g) : + basicOpen f = ψ ''ᵁ basicOpen (a f) := by + rw [← SpecMap_preimage_basicOpen, Scheme.Hom.image_preimage_eq_opensRange_inf] + simp [a, ψ, hf] + let iso : Γ(M.restrict ψ, ⊤) ≅ Γ(M, basicOpen g) := + M.restrictAppIso _ _ ≪≫ M.presheaf.mapIso (eqToIso <| by simp [ψ, a]).op + let e (f : R) (hf : basicOpen f ≤ basicOpen g) : Γ(M', basicOpen (a f)) ≅ Γ(M, basicOpen f) := + M.restrictAppIso ψ (basicOpen (a f)) ≪≫ M.presheaf.mapIso (eqToIso <| heq f hf).op + refine ⟨fun f hf s ↦ ?_, fun f hf t ht ↦ ?_⟩ + · obtain ⟨n, t, ht⟩ := h.existence (a f) le_top ((e _ hf).inv s) + use n, iso.hom t + have := congr((e _ hf).hom $ht) + dsimp [M'] at this + rw [← ConcreteCategory.comp_apply] at this + simp only [homOfLE_leOfHom, Iso.trans_hom, Functor.mapIso_hom, Iso.op_hom, eqToIso.hom, + eqToHom_op, Iso.trans_inv, Functor.mapIso_inv, Iso.op_inv, eqToIso.inv, e, iso] at this ⊢ + simp only [homOfLE_leOfHom, Scheme.Modules.map_restrictAppIso_hom_assoc, AddCommGrpCat.hom_comp, + AddMonoidHom.coe_comp, Function.comp_apply, ← map_pow, ψ] at this + rw [Scheme.Modules.restrictAppIso_smul_Spec] at this + simpa [← Functor.map_comp_apply, eqToHom_comp_homOfLE_op, homOfLE_op_comp_eqToHom] using this + · obtain ⟨n, hn⟩ := h.uniqueness (a f) le_top (iso.inv t) <| by + simpa [M', iso, ← M.presheaf.map_comp_apply, homOfLE_op_comp_eqToHom, e] using + congr((e _ hf).inv $ht) + use n + have := congr(iso.hom $hn) + dsimp [iso, ψ] at this + rw [eqToHom_op, map_zero, ← map_pow, Scheme.Modules.restrictAppIso_smul_Spec, + M.map_smul_Spec, Iso.inv_hom_id_apply] at this + simpa using this + +end QuasicoherentTilde + +open QuasicoherentTilde in +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +/-- If `M` is a quasi-coherent `𝒪_{Spec R}` module, it is isomorphic to `Γ(M)^~`. -/ +instance Scheme.Modules.isIso_fromTildeΓ_of_isQuasicoherent (M : (Spec R).Modules) + [M.IsQuasicoherent] : IsIso M.fromTildeΓ := by + rw [isIso_fromTildeΓ_iff_isLocalizing, isLocalizing_iff_aux] + obtain ⟨ι, U, pres, hU, hU'⟩ := M.exists_isOpenCover_presentation + obtain ⟨s, hs⟩ := hU.exists_finite_of_compactSpace + choose κ hκ a ha using fun i : s ↦ + PrimeSpectrum.isBasis_basic_opens.exists_iSup_eq_of_isCompact (U i) (hU' i).isCompact + refine Aux.of_eq_iSup_basicOpen _ (fun i : Sigma κ ↦ a _ i.2) ?_ ?_ + · rw [IsOpenCover] at hs + rw [eq_comm, iSup_sigma, ← hs] + exact iSup_congr fun i ↦ (ha i).symm + · intro i + let t := (Spec R).homOfLE (U := PrimeSpectrum.basicOpen (a _ i.2)) (V := U i.1) + (by rw [ha]; exact le_iSup_of_le _ le_rfl) + let iso : restrictFunctor (U i.1).ι ⋙ restrictFunctor ((basicOpenIsoSpecAway _).inv ≫ t) ≅ + restrictFunctor (Spec.map (CommRingCat.ofHom <| algebraMap _ _)) := + (restrictFunctorComp _ _).symm ≪≫ + restrictFunctorCongr (by simp [t, basicOpenIsoSpecAway]) + let pres := SheafOfModules.Presentation.ofIsIso.{u, u, u} (iso.app M).hom <| + presentationRestrict ((basicOpenIsoSpecAway _).inv ≫ t) (pres i.1) + have : IsIso _ := isIso_fromTildeΓ_of_presentation (M.restrict _) pres + rw [isIso_fromTildeΓ_iff_isLocalizing, isLocalizing_iff_aux] at this + exact aux_basicOpen_of_aux_restrict _ _ this + +set_option backward.isDefEq.respectTransparency false in +/-- An `𝒪_{Spec R}` module `M` is quasicoherent if and only if it is isomorphic to `Γ(M)^~`. -/ +theorem isQuasicoherent_iff_isIso_fromTildeΓ (M : (Spec R).Modules) : + M.IsQuasicoherent ↔ IsIso M.fromTildeΓ := by + refine ⟨fun h ↦ inferInstance, fun h ↦ ?_⟩ + exact (SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf).prop_of_iso + (asIso <| M.fromTildeΓ) inferInstance + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +lemma essImage_tilde : (tilde.functor R).essImage = + SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf := by + refine le_antisymm ?_ ?_ + · intro M ⟨N, ⟨e⟩⟩ + exact (SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf).prop_of_iso e + (by dsimp; infer_instance) + · intro M (h : M.IsQuasicoherent) + exact ⟨((modulesSpecToSheaf.obj M).presheaf.obj (.op ⊤)), ⟨asIso <| M.fromTildeΓ⟩⟩ + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +/-- `M ↦ M^~` is an equivalence of categories from `ModuleCat R` to the full subcategory +of quasi-coherent `𝒪_{Spec R}`-modules. -/ +@[simps! functor inverse unitIso counitIso_hom_app_hom] +def tildeEquiv : + ModuleCat R ≌ (SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf).FullSubcategory where + functor := ObjectProperty.lift _ (tilde.functor R) fun _ ↦ by + dsimp [SheafOfModules.isQuasicoherent] + infer_instance + inverse := ObjectProperty.ι _ ⋙ moduleSpecΓFunctor (R := R) + unitIso := tilde.toTildeΓNatIso + counitIso := + haveI (M : (SheafOfModules.isQuasicoherent (Spec R).ringCatSheaf).FullSubcategory) : + IsIso (Scheme.Modules.fromTildeΓ M.obj) := inferInstance + NatIso.ofComponents + (fun M ↦ ObjectProperty.isoMk _ (asIso <| Scheme.Modules.fromTildeΓ M.obj)) + fun f ↦ ObjectProperty.hom_ext _ (tilde.adjunction (R := R).counit.naturality f.hom) + functor_unitIso_comp M := + ObjectProperty.hom_ext _ (tilde.adjunction (R := R).left_triangle_components M) + end IsQuasicoherent end AlgebraicGeometry diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean b/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean index 86a83253b590f5..e57a1ed93c8f18 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/Finite.lean @@ -154,8 +154,7 @@ instance {U V X : Scheme.{u}} (f : U ⟶ X) (g : V ⟶ X) [IsFinite f] [IsFinite end IsFinite -lemma Scheme.Hom.finite_appTop {X Y : Scheme.{u}} (f : X ⟶ Y) [IsAffine X] [IsAffine Y] - [IsFinite f] : +lemma Scheme.Hom.finite_appTop {X Y : Scheme.{u}} (f : X ⟶ Y) [IsAffine Y] [IsFinite f] : f.appTop.hom.Finite := (HasAffineProperty.iff_of_isAffine (P := @IsFinite).mp inferInstance).2 diff --git a/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean b/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean index b8e5ffa0b1bd1b..095c53dd0d0a98 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/Flat.lean @@ -270,7 +270,7 @@ lemma mono_pushoutSection_of_iSup_eq {ι : Type*} [Finite ι] (VX : ι → X.Ope let ψY : Γ(Y, UY) →+* Π i, Γ(Y, g ⁻¹ᵁ VX i ⊓ iY ⁻¹ᵁ UT) := RingHom.pi fun i ↦ (Y.presheaf.map (homOfLE (by subst hUY hVU; gcongr; exact le_iSup _ _)).op).hom -- The map `Γ(X, U) ⟶ ∏ᵢ Γ(X, Vᵢ)` - let ψ : Γ(X, UX) →ₐ[Γ(S, US)] Π i, Γ(X, VX i) := Pi.algHom _ _ fun i ↦ + let ψ : Γ(X, UX) →ₐ[Γ(S, US)] Π i, Γ(X, VX i) := AlgHom.pi fun i ↦ ⟨(X.presheaf.map (homOfLE (hVU ▸ le_iSup VX i)).op).hom, fun r ↦ by dsimp [RingHom.algebraMap_toAlgebra] simp only [← CommRingCat.comp_apply, Scheme.Hom.appLE_map]⟩ diff --git a/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean b/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean index 897283b0a8f0f7..49fe5adb97ebef 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/FormallyUnramified.lean @@ -212,7 +212,7 @@ protected lemma hom_ext {Z' Z : Scheme} (i : Z' ⟶ Z) (hi : IsNilpotent i.ker) Scheme.Hom.appLE_comp_appLE]⟩ let ψ₂ : Γ(X, V) →ₐ[Γ(Y, U)] Γ(Z, W) := ⟨(g₂.appLE _ _ (hWV.trans inf_le_right)).hom, fun r ↦ by simp [RingHom.algebraMap_toAlgebra, ← CategoryTheory.comp_apply, -CommRingCat.hom_comp, - Scheme.Hom.appLE_comp_appLE, hgf, - Scheme.Hom.comp_appLE]⟩ + Scheme.Hom.appLE_comp_appLE, hgf, -Scheme.Hom.comp_appLE]⟩ suffices ψ₁ = ψ₂ by simpa [ψ₁, ψ₂, -Iso.cancel_iso_hom_left, IsAffineOpen.isoSpec_hom] using congr(hW.isoSpec.hom ≫ Spec.map (CommRingCat.ofHom ($this).toRingHom) ≫ hV.fromSpec) @@ -220,7 +220,7 @@ protected lemma hom_ext {Z' Z : Scheme} (i : Z' ⟶ Z) (hi : IsNilpotent i.ker) · obtain ⟨n, hn⟩ := hi exact ⟨n, by simpa using congr(($hn).ideal ⟨W, hW⟩)⟩ · simp [ψ₁, ψ₂, ← CategoryTheory.comp_apply, -CommRingCat.hom_comp, hig, - Scheme.Hom.app_eq_appLE, Scheme.Hom.appLE_comp_appLE, - Scheme.Hom.comp_appLE] + Scheme.Hom.app_eq_appLE, Scheme.Hom.appLE_comp_appLE, -Scheme.Hom.comp_appLE] /-- To show that `f : X ⟶ Y` is formally unramified, diff --git a/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean b/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean index f505798c3260fe..64b3f6a6381810 100644 --- a/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean +++ b/Mathlib/AlgebraicGeometry/Morphisms/UnderlyingMap.lean @@ -314,8 +314,8 @@ end SpecializingMap section GeneralizingMap instance : (topologically GeneralizingMap).RespectsIso := - topologically_respectsIso _ (fun f ↦ f.isOpenEmbedding.generalizingMap - f.isOpenEmbedding.isOpen_range.stableUnderGeneralization) (fun _ _ hf hg ↦ hf.comp hg) + topologically_respectsIso _ (fun f ↦ f.isOpenEmbedding.generalizingMap) + (fun _ _ hf hg ↦ hf.comp hg) instance : IsZariskiLocalAtSource (topologically GeneralizingMap) := topologically_isZariskiLocalAtSource' (fun _ ↦ _) fun _ _ _ hU _ ↦ hU.generalizingMap_iff_comp diff --git a/Mathlib/AlgebraicGeometry/OpenImmersion.lean b/Mathlib/AlgebraicGeometry/OpenImmersion.lean index aede7ae8f77c45..a8b61e1491ec98 100644 --- a/Mathlib/AlgebraicGeometry/OpenImmersion.lean +++ b/Mathlib/AlgebraicGeometry/OpenImmersion.lean @@ -8,6 +8,7 @@ module public import Mathlib.Geometry.RingedSpace.OpenImmersion public import Mathlib.AlgebraicGeometry.Scheme public import Mathlib.CategoryTheory.MorphismProperty.Limits +public import Mathlib.CategoryTheory.Limits.Preorder /-! # Open immersions of schemes @@ -87,6 +88,37 @@ theorem mem_opensRange {f : X ⟶ Y} [IsOpenImmersion f] {y : Y} : def opensFunctor : X.Opens ⥤ Y.Opens := LocallyRingedSpace.IsOpenImmersion.opensFunctor f.toLRSHom +/-- The adjunction image-preimage adjunction for an open immersion of schemes. -/ +def opensFunctorAdjunction : f.opensFunctor ⊣ TopologicalSpace.Opens.map f.base := + IsOpenMap.adjunction ‹IsOpenImmersion f›.base_open.isOpenMap + +instance : f.opensFunctor.IsLeftAdjoint := + f.opensFunctorAdjunction.isLeftAdjoint + +instance : f.opensFunctor.IsCocontinuous (Opens.grothendieckTopology _) + (Opens.grothendieckTopology _) := by + rw [f.opensFunctorAdjunction.isCocontinuous_iff_coverPreserving] + exact coverPreserving_opens_map f.base + +instance : f.opensFunctor.Full := + have : Mono f.base := (TopCat.mono_iff_injective f.base).mpr f.isOpenEmbedding.injective + inferInstanceAs f.isOpenEmbedding.functor.Full + +lemma coverPreserving_opensFunctor : + CoverPreserving (Opens.grothendieckTopology _) (Opens.grothendieckTopology _) f.opensFunctor := + f.isOpenEmbedding.isOpenMap.coverPreserving + +instance {X Y : Scheme.{u}} (f : X ⟶ Y) [IsOpenImmersion f] : + PreservesLimitsOfShape WalkingCospan (Scheme.Hom.opensFunctor f) := by + dsimp [Scheme.Hom.opensFunctor] + infer_instance + +instance {X Y : Scheme.{u}} (f : X ⟶ Y) [IsOpenImmersion f] : + f.opensFunctor.PreservesOneHypercovers (Opens.grothendieckTopology _) + (Opens.grothendieckTopology _) := by + refine Functor.PreservesOneHypercovers.of_coverPreserving ?_ + exact Scheme.Hom.coverPreserving_opensFunctor f + /-- `f ''ᵁ U` is notation for the image (as an open set) of `U` under an open immersion `f`. The preferred name in lemmas is `image` and it should be treated as an infix. -/ scoped[AlgebraicGeometry] notation3:90 f:91 " ''ᵁ " U:90 => (Scheme.Hom.opensFunctor f).obj U @@ -206,6 +238,13 @@ theorem appIso_hom' (U) : (f.appIso U).hom = f.appLE (f ''ᵁ U) U (preimage_image_eq f U).ge := f.appIso_hom U +set_option backward.defeqAttrib.useBackward true in +@[reassoc (attr := simp)] +lemma appIso_hom_naturality {U V : X.Opens} (i : op U ⟶ op V) : + dsimp% Y.presheaf.map (f.opensFunctor.op.map i) ≫ (f.appIso V).hom = + (f.appIso U).hom ≫ X.presheaf.map i := by + simp [← cancel_mono (f.appIso V).inv] + @[reassoc (attr := simp)] theorem app_appIso_inv (U) : f.app U ≫ (f.appIso (f ⁻¹ᵁ U)).inv = @@ -288,6 +327,13 @@ instance {R} [CommRing R] (f : R) : IsOpenImmersion (Spec.map (CommRingCat.ofHom (algebraMap R (Localization.Away f)))) := isOpenImmersion_SpecMap_localizationAway (R := .of R) f +@[simp] +lemma Hom.opensRange_localizationAway {R : CommRingCat.{u}} (g : R) : + (Spec.map <| CommRingCat.ofHom <| algebraMap R (Localization.Away g)).opensRange = + PrimeSpectrum.basicOpen g := by + rw [SetLike.ext'_iff] + exact PrimeSpectrum.localization_away_comap_range _ g + lemma _root_.AlgebraicGeometry.IsOpenImmersion.of_isLocalization {R S} [CommRing R] [CommRing S] [Algebra R S] (f : R) [IsLocalization.Away f S] : IsOpenImmersion (Spec.map (CommRingCat.ofHom (algebraMap R S))) := by diff --git a/Mathlib/AlgebraicGeometry/OrderOfVanishing.lean b/Mathlib/AlgebraicGeometry/OrderOfVanishing.lean new file mode 100644 index 00000000000000..3fb564c22a695d --- /dev/null +++ b/Mathlib/AlgebraicGeometry/OrderOfVanishing.lean @@ -0,0 +1,135 @@ +/- +Copyright (c) 2025 Raphael Douglas Giles. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Raphael Douglas Giles +-/ +module + +public import Mathlib.AlgebraicGeometry.FunctionField +public import Mathlib.AlgebraicGeometry.Noetherian +public import Mathlib.RingTheory.OrderOfVanishing.Noetherian + +/-! +# Order of vanishing in a scheme + +In this file we define the order of vanishing of an element of the function field of a locally +Noetherian integral scheme at a point of codimension `1`. +-/ + +@[expose] public section + +open WithZero AlgebraicGeometry Order TopologicalSpace CategoryTheory + +universe u + +variable {X : Scheme.{u}} + +namespace AlgebraicGeometry.Scheme + +variable [IsIntegral X] [IsLocallyNoetherian X] + +/-- +Order of vanishing on a locally Noetherian integral scheme as a monoid with zero hom to `ℤᵐ⁰`. +-/ +noncomputable +def ordHom (z : X) (hz : coheight z = 1) : X.functionField →*₀ ℤᵐ⁰ := + haveI : Ring.KrullDimLE 1 (X.presheaf.stalk z) := krullDimLE_of_coheight_le hz.le + Ring.ordFrac (X.presheaf.stalk z) + +lemma ordHom_of_isUnit {U : X.Opens} + [Nonempty U] {f : Γ(X, U)} (hf : IsUnit f) {x : X} (hx : coheight x = 1) (hx' : x ∈ U) : + ordHom x hx (X.germToFunctionField U f) = 1 := by + have : Ring.KrullDimLE 1 (X.presheaf.stalk x) := krullDimLE_of_coheight_le hx.le + rw [← algebraMap_germ_eq_germToFunctionField _ hx'] + exact Ring.ordFrac_of_isUnit (hf.map (X.presheaf.germ U x hx').hom) + +/-- +The order of vanishing of an element of the function field of a locally Noetherian integral scheme +at a point. This has a junk value of `0` if `f = 0` or if `coheight z ≠ 1`. +-/ +@[no_expose] +noncomputable +def ord (f : X.functionField) (z : X) : ℤ := + if hz : coheight z = 1 + then Multiplicative.toAdd <| (X.ordHom z hz f).unzeroD 1 + else 0 + +lemma ord_eq_ordHom_of_coheight_eq_one {z : X} (hz : coheight z = 1) (f : X.functionField) : + ord f z = Multiplicative.toAdd ((X.ordHom z hz f).unzeroD 1) := dif_pos hz + +@[simp] +lemma ord_eq_zero_of_coheight_neq_one {z : X} (hz : coheight z ≠ 1) (f : X.functionField) : + ord f z = 0 := dif_neg hz + +@[simp] +lemma ord_zero : ord (0 : X.functionField) = 0 := by + ext z + by_cases h : coheight z = 1 + · simp [ord_eq_ordHom_of_coheight_eq_one h, unzeroD] + · simp [h] + +lemma ord_eq_unzero_ordHom {x : X} (hx : coheight x = 1) {f : X.functionField} (hf : f ≠ 0) : + ord f x = (WithZero.unzero ((map_ne_zero (ordHom x hx)).mpr hf)).toAdd := by + simp [ord, hx, unzeroD_eq_unzero ((map_ne_zero (ordHom x hx)).mpr hf)] + +lemma ord_eq_iff {z : X} (hz : coheight z = 1) {f : X.functionField} (hf : f ≠ 0) {n : ℤ} : + ord f z = n ↔ ordHom z hz f = Multiplicative.ofAdd n := by + rw [ord_eq_unzero_ordHom hz hf] + exact WithZero.toAdd_unzero_eq_iff _ _ + +@[simp] +lemma ord_mul {x : X} {f g : X.functionField} + (hf : f ≠ 0) (hg : g ≠ 0) : ord (f * g) x = ord f x + ord g x := by + by_cases! hx : coheight x ≠ 1 + · simp [hx] + rw [ord_eq_iff hx <| (mul_ne_zero_iff_right hg).mpr hf] + simp [hf, hg, ord_eq_ordHom_of_coheight_eq_one hx, unzeroD_eq_unzero] + +lemma ord_of_isUnit {U : X.Opens} [Nonempty U] {f : Γ(X, U)} (hf : IsUnit f) {x : X} + (hx' : x ∈ U) : ord (X.germToFunctionField U f) x = 0 := by + by_cases! hx : coheight x ≠ 1 + · simp [hx] + simp [map_ne_zero_iff, germToFunctionField_injective, IsUnit.ne_zero hf, + ord_eq_iff hx, ordHom_of_isUnit hf hx hx'] + +lemma ord_le_ord_iff {x y : X} (hx : coheight x = 1) (hy : coheight y = 1) {f g : X.functionField} + (hf : f ≠ 0) (hg : g ≠ 0) : + ord f x ≤ ord g y ↔ ordHom x hx f ≤ ordHom y hy g := by + simp [ord_eq_unzero_ordHom hx hf, ord_eq_unzero_ordHom hy hg, Multiplicative.toAdd_le] + +lemma le_ord_iff {x : X} (hx : coheight x = 1) {f : X.functionField} + (hf : f ≠ 0) {n : ℤ} : + n ≤ ord f x ↔ Multiplicative.ofAdd n ≤ ordHom x hx f := by + rw [ord_eq_unzero_ordHom hx hf] + nth_rw 1 [← toAdd_ofAdd n] + rw [Multiplicative.toAdd_le, le_unzero_iff] + +lemma ord_add {x : X} [IsDiscreteValuationRing (X.presheaf.stalk x)] + {f g : X.functionField} (hfg : f + g ≠ 0) : + min (ord f x) (ord g x) ≤ ord (f + g) x := by + by_cases hf : f = 0 + · simp [hf] + by_cases hg : g = 0 + · simp [hg] + by_cases! hx : coheight x ≠ 1 + · simp [hx] + rw [inf_le_iff, ord_le_ord_iff hx hx hf hfg, ord_le_ord_iff hx hx hg hfg] + exact inf_le_iff.mp <| Ring.ordFrac_add (R := X.presheaf.stalk x) _ _ hfg + +lemma ord_le_smul {x : X} {U : X.Opens} [Nonempty U] (hxU : x ∈ U) + {a : Γ(X, U)} (ha : a ≠ 0) (f : X.functionField) : ord f x ≤ ord (a • f) x := by + by_cases! hx : coheight x ≠ 1 + · simp [hx] + by_cases hf : f = 0 + · simp [hf] + have : a • f ≠ 0 := by simp [ha, Algebra.smul_def, hf, germToFunctionField_injective, + RingHom.algebraMap_toAlgebra, map_ne_zero_iff] + rw [ord_le_ord_iff hx hx hf this] + algebraize [(X.presheaf.germ U x hxU).hom] + have : Ring.KrullDimLE 1 ↑(X.presheaf.stalk x) := krullDimLE_of_coheight_le hx.le + have : IsScalarTower ↑Γ(X, U) ↑(X.presheaf.stalk x) ↑X.functionField := + functionField_isScalarTower X U ⟨x, hxU⟩ + simp [ordHom, Ring.ordFrac_le_smul, RingHom.algebraMap_toAlgebra, map_ne_zero_iff, + germ_injective_of_isIntegral, ha] + +end AlgebraicGeometry.Scheme diff --git a/Mathlib/AlgebraicGeometry/Properties.lean b/Mathlib/AlgebraicGeometry/Properties.lean index bd9f2c72578d47..6d7e1302696a37 100644 --- a/Mathlib/AlgebraicGeometry/Properties.lean +++ b/Mathlib/AlgebraicGeometry/Properties.lean @@ -386,6 +386,13 @@ lemma ringKrullDim_stalk_eq_coheight {X : Scheme} (x : X) : apply WithBot.coe_eq_coe.mpr exact idealHeight_eq_coheight R x +open Order in +variable {X} in +lemma krullDimLE_of_coheight_le + {z : X} {n : ℕ} (hz : coheight z ≤ n) : Ring.KrullDimLE n (X.presheaf.stalk z) := by + rw [Ring.krullDimLE_iff, ringKrullDim_stalk_eq_coheight z] + exact_mod_cast hz + lemma isField_of_isIntegral_of_subsingleton (X : Scheme.{u}) [IsIntegral X] [Subsingleton X] : IsField Γ(X, ⊤) := by rw [← PrimeSpectrum.t1Space_iff_isField] diff --git a/Mathlib/AlgebraicGeometry/ResidueField.lean b/Mathlib/AlgebraicGeometry/ResidueField.lean index 3aa4861abd5d33..20c400e69b5ca9 100644 --- a/Mathlib/AlgebraicGeometry/ResidueField.lean +++ b/Mathlib/AlgebraicGeometry/ResidueField.lean @@ -140,6 +140,20 @@ lemma residueFieldMap_comp {Z : Scheme.{u}} (g : Y ⟶ Z) (x : X) : (f ≫ g).residueFieldMap x = g.residueFieldMap (f x) ≫ f.residueFieldMap x := LocallyRingedSpace.residueFieldMap_comp _ _ _ +/-- +Degree of `f` at a point `x` is defined to be the degree of the associated field extension +from `κ(f x)` to `κ(x)`. We return a default value of zero when this degree is infinite. +-/ +def Hom.residueDegree (f : X ⟶ Y) (x : X) : ℕ := + letI := (f.residueFieldMap x).hom.toAlgebra + Module.finrank (Y.residueField (f x)) (X.residueField x) + +@[simp] +lemma Hom.residueDegree_id (x : X) : (𝟙 _ : X ⟶ X).residueDegree x = 1 := by + dsimp [residueDegree] + rw [residueFieldMap_id] + exact CommSemiring.finrank_self _ + @[reassoc] lemma evaluation_naturality {V : Opens Y} (x : X) (hx : f x ∈ V) : Y.evaluation V (f x) hx ≫ f.residueFieldMap x = diff --git a/Mathlib/AlgebraicGeometry/Restrict.lean b/Mathlib/AlgebraicGeometry/Restrict.lean index a8ccc83681a2f8..6e9b51c80d71e1 100644 --- a/Mathlib/AlgebraicGeometry/Restrict.lean +++ b/Mathlib/AlgebraicGeometry/Restrict.lean @@ -59,6 +59,8 @@ instance : IsOpenImmersion U.ι := inferInstanceAs (IsOpenImmersion (X.ofRestric @[simps! over] instance : U.toScheme.CanonicallyOver X where hom := U.ι +lemma ι_comp_over (S : Scheme.{u}) [X.Over S] : U.ι ≫ X ↘ S = U.toScheme ↘ S := rfl + instance (U : X.Opens) : U.ι.IsOver X where lemma toScheme_carrier : (U : Type u) = (U : Set X) := rfl @@ -307,6 +309,19 @@ instance (X : Scheme.{u}) {U V : X.Opens} (e : U ≤ V) : IsOpenImmersion (X.hom delta Scheme.homOfLE infer_instance +set_option backward.isDefEq.respectTransparency false in +lemma Scheme.Hom.appIso_homOfLE_inv {X : Scheme.{u}} {U V : X.Opens} (h : U ≤ V) + (W : (U : Scheme.{u}).Opens) : + ((X.homOfLE h).appIso W).inv = + X.presheaf.map (.op <| homOfLE <| by + suffices V.ι ''ᵁ _ ≤ U.ι ''ᵁ W by simpa + simp [← Scheme.Hom.comp_image]) := by + rw [eq_comm, ← Iso.hom_comp_eq_id] + dsimp + simp only [appIso_hom, homOfLE_app, homOfLE_leOfHom, eqToHom_op, Opens.toScheme_presheaf_map, + eqToHom_unop, ← X.presheaf.map_comp, Category.assoc, ← X.presheaf.map_id] + rfl + @[simp] lemma Scheme.opensRange_homOfLE {U V : X.Opens} (e : U ≤ V) : (X.homOfLE e).opensRange = V.ι ⁻¹ᵁ U := @@ -504,6 +519,12 @@ def basicOpenIsoSpecAway {R : CommRingCat.{u}} (f : R) : simp only [Scheme.Opens.range_ι] exact (PrimeSpectrum.localization_away_comap_range _ _).symm) +@[reassoc (attr := simp)] +lemma basicOpenIsoSpecAway_hom_SpecMap {R : CommRingCat.{u}} (f : R) : + (basicOpenIsoSpecAway f).hom ≫ Spec.map (CommRingCat.ofHom (algebraMap R _)) = + Scheme.Opens.ι (X := Spec R) (PrimeSpectrum.basicOpen f) := by + simp [basicOpenIsoSpecAway] + set_option backward.isDefEq.respectTransparency false in @[reassoc] lemma basicOpenIsoSpecAway_inv_homOfLE {R : CommRingCat.{u}} (f g x : R) (hx : x = f * g) : diff --git a/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean b/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean index 3e343f60a1f49d..77045d93db95c8 100644 --- a/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean +++ b/Mathlib/AlgebraicGeometry/Sites/Fpqc.lean @@ -97,7 +97,7 @@ instance : fppfTopology.Subcanonical := lemma Hom.singleton_mem_fppfPrecoverage {X Y : Scheme.{u}} (f : X ⟶ Y) [Flat f] [Surjective f] [LocallyOfFinitePresentation f] : Presieve.singleton f ∈ fppfPrecoverage Y := by - rw [← Presieve.ofArrows_pUnit.{_, _, 0}] + rw [← Presieve.ofArrows_pUnit.{0}] exact (f.cover (P := @Flat ⊓ @LocallyOfFinitePresentation) ⟨‹_›, ‹_›⟩).mem₀ @[simp] diff --git a/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean b/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean index 7fb519d3c2dbe7..bc84f286e65bbc 100644 --- a/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean +++ b/Mathlib/AlgebraicGeometry/Sites/MorphismProperty.lean @@ -88,7 +88,7 @@ lemma ofArrows_mem_precoverage_iff {S : Scheme.{u}} {ι : Type*} {X : ι → Sch @[simp] lemma singleton_mem_precoverage_iff {X S : Scheme.{u}} (f : X ⟶ S) : Presieve.singleton f ∈ precoverage P S ↔ Function.Surjective f.base ∧ P f := by - rw [← Presieve.ofArrows_pUnit.{_, _, 0}, ofArrows_mem_precoverage_iff] + rw [← Presieve.ofArrows_pUnit.{0}, ofArrows_mem_precoverage_iff] aesop lemma bot_mem_precoverage (X : Scheme.{u}) [IsEmpty X] : ⊥ ∈ Scheme.precoverage P X := diff --git a/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean b/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean index b65e9b431853f1..a4dd9abc9cce9c 100644 --- a/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean +++ b/Mathlib/AlgebraicTopology/FundamentalGroupoid/FundamentalGroup.lean @@ -32,17 +32,19 @@ variable (X) /-- The fundamental group is the automorphism group (vertex group) of the basepoint in the fundamental groupoid. -/ -def FundamentalGroup (x : X) := +abbrev FundamentalGroup (x : X) := End (FundamentalGroupoid.mk x) -instance (x : X) : Group (FundamentalGroup X x) := inferInstanceAs (Group (End _)) - -instance (x : X) : Inhabited (FundamentalGroup X x) := inferInstanceAs (Inhabited (End _)) - variable {X} namespace FundamentalGroup +variable {x : X} {p q : FundamentalGroup X x} + +theorem one_def : (1 : FundamentalGroup X x) = .refl x := rfl +theorem mul_def : p * q = q.trans p := rfl +theorem inv_def : p⁻¹ = p.symm := rfl + /-- Get an isomorphism between the fundamental groups at two points given a path -/ def fundamentalGroupMulEquivOfPath (p : Path x₀ x₁) : FundamentalGroup X x₀ ≃* FundamentalGroup X x₁ := @@ -84,8 +86,8 @@ variable (f : C(X, Y)) {x : X} {y : Y} (h : f x = y) def mapOfEq : FundamentalGroup X x →* FundamentalGroup Y y := (eqToIso <| congr_arg FundamentalGroupoid.mk h).conj.toMonoidHom.comp (map f x) -theorem mapOfEq_apply (p : Path x x) : - mapOfEq f h (fromPath <| .mk p) = fromPath (.mk <| (p.map f.continuous).cast h.symm h.symm) := +theorem mapOfEq_apply (p : FundamentalGroup X x) : + mapOfEq f h p = (Path.Homotopic.Quotient.map p f).cast h.symm h.symm := FundamentalGroupoid.conj_eqToHom .. end FundamentalGroup diff --git a/Mathlib/AlgebraicTopology/FundamentalGroupoid/Product.lean b/Mathlib/AlgebraicTopology/FundamentalGroupoid/Product.lean index 210b35e6d26059..42bd9e368c72b8 100644 --- a/Mathlib/AlgebraicTopology/FundamentalGroupoid/Product.lean +++ b/Mathlib/AlgebraicTopology/FundamentalGroupoid/Product.lean @@ -12,6 +12,7 @@ public import Mathlib.Topology.Homotopy.Product /-! # Fundamental groupoid preserves products + In this file, we give the following definitions/theorems: - `FundamentalGroupoidFunctor.piIso` An isomorphism between Π i, (π Xᵢ) and π (Πi, Xᵢ), whose diff --git a/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean b/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean index c22d642451c080..55c5697d31858a 100644 --- a/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean +++ b/Mathlib/AlgebraicTopology/FundamentalGroupoid/SimplyConnected.lean @@ -13,6 +13,7 @@ public import Mathlib.AlgebraicTopology.FundamentalGroupoid.PUnit /-! # Simply connected spaces + This file defines simply connected spaces. A topological space is simply connected if its fundamental groupoid is equivalent to `Unit`. @@ -67,7 +68,7 @@ instance (x y : X) : Subsingleton (Path.Homotopic.Quotient x y) := rw [simply_connected_iff_unique_homotopic] at *; tauto)) instance (x : X) : Subsingleton (FundamentalGroup X x) := - show Subsingleton (Path.Homotopic.Quotient x x) from inferInstance + inferInstanceAs <| Subsingleton (Path.Homotopic.Quotient x x) instance (priority := 100) : PathConnectedSpace X := let unique_homotopic := (simply_connected_iff_unique_homotopic X).mp inferInstance diff --git a/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean b/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean index b088b3d15ccb4b..bd654074d1fbd5 100644 --- a/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean +++ b/Mathlib/AlgebraicTopology/ModelCategory/CategoryWithCofibrations.lean @@ -267,6 +267,10 @@ instance [(weakEquivalences C).IsMultiplicative] : (weakEquivalences P.FullSubcategory).IsMultiplicative := inferInstanceAs ((weakEquivalences C).inverseImage P.ι).IsMultiplicative +instance [(weakEquivalences C).RespectsIso] : + (weakEquivalences P.FullSubcategory).RespectsIso := + inferInstanceAs ((weakEquivalences C).inverseImage P.ι).RespectsIso + lemma weakEquivalence_iff_of_objectProperty {X Y : P.FullSubcategory} (f : X ⟶ Y) : WeakEquivalence f ↔ WeakEquivalence f.hom := by diff --git a/Mathlib/AlgebraicTopology/Reedy/Basic.lean b/Mathlib/AlgebraicTopology/Reedy/Basic.lean new file mode 100644 index 00000000000000..625b0026c3d70a --- /dev/null +++ b/Mathlib/AlgebraicTopology/Reedy/Basic.lean @@ -0,0 +1,228 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou, Nima Rasekh, Aras Ergus +-/ +module + +public import Mathlib.CategoryTheory.MorphismProperty.Composition +public import Mathlib.CategoryTheory.MorphismProperty.Factorization +public import Mathlib.CategoryTheory.Skeletal +public import Mathlib.Order.SuccPred.Basic + +/-! +# Reedy categories + +In this file, we introduce the definition of a Reedy structure +on a category `C` equipped with two classes of morphisms +`W₁` and `W₂` (these are sometimes denoted `C₋` and `C₊` in +the literature). + +## TODO +* Construct the Reedy model category structure on the category of +functors `C ⥤ D` when `C` is a Reedy category and `D` a model category +https://github.com/leanprover-community/project-intentions/issues/5 + +## References +* [Emily Riehl and Dominic Verity, *Elements of ∞-Category Theory*, C.4][RiehlVerity2022] + +-/ + +@[expose] public section + +universe u + +open CategoryTheory + +namespace HomotopicalAlgebra + +open MorphismProperty in +/-- A Reedy structure on a category `C` equipped with two multiplicative +classes of morphisms `W₁` and `W₂` consists of the data of a degree +map for objects `deg : C → α`, where `α` is a well ordered type. The first +two axioms `lt₁` and `lt₂` express the behaviour of the degree with +respect to morphisms in `W₁` (resp. `W₂`) that are not identities, and +the last axiom says that any morphism can be factored in a unique way +as a morphism in `W₁` followed by a morphism in `W₂`. -/ +structure ReedyStructure {C : Type*} [Category* C] (W₁ W₂ : MorphismProperty C) + [W₁.IsMultiplicative] [W₂.IsMultiplicative] + (α : Type*) [LinearOrder α] [OrderBot α] [SuccOrder α] [WellFoundedLT α] where + /-- the degree of an object -/ + deg : C → α + lt₁ {X Y : C} (f : X ⟶ Y) (hf : W₁ f) (hf' : ¬ identities C f) : deg Y < deg X + lt₂ {X Y : C} (f : X ⟶ Y) (hf : W₂ f) (hf' : ¬ identities C f) : deg X < deg Y + nonempty_unique {X Y : C} (f : X ⟶ Y) : + Nonempty (Unique (W₁.MapFactorizationData W₂ f)) + +namespace ReedyStructure + +variable {C : Type*} [Category* C] {W₁ W₂ : MorphismProperty C} + [W₁.IsMultiplicative] [W₂.IsMultiplicative] + {α : Type*} [LinearOrder α] [OrderBot α] [SuccOrder α] [WellFoundedLT α] + (r : ReedyStructure W₁ W₂ α) + +/-- The opposite of a Reedy structure. -/ +@[simps] +protected def op : ReedyStructure W₂.op W₁.op α where + deg := r.deg ∘ Opposite.unop + lt₁ f hf hf' := r.lt₂ f.unop hf (by + simpa [MorphismProperty.identities_op_iff] using hf') + lt₂ f hf hf' := r.lt₁ f.unop hf (by + simpa [MorphismProperty.identities_op_iff] using hf') + nonempty_unique f := + MorphismProperty.MapFactorizationData.opEquiv.uniqueCongr.nonempty_congr.1 + (r.nonempty_unique f.unop) + +lemma le₁ {X Y : C} (f : X ⟶ Y) (hf : W₁ f) : r.deg Y ≤ r.deg X := by + by_cases hf' : MorphismProperty.identities C f + · cases hf' + rfl + · exact (r.lt₁ f hf hf').le + +lemma le₂ {X Y : C} (f : X ⟶ Y) (hf : W₂ f) : r.deg X ≤ r.deg Y := by + by_cases hf' : MorphismProperty.identities C f + · cases hf' + rfl + · exact (r.lt₂ f hf hf').le + +lemma identities_of_prop₁_of_eq {X Y : C} {f : X ⟶ Y} (hf : W₁ f) (h : r.deg X = r.deg Y) : + MorphismProperty.identities _ f := by + by_contra + exact h.not_gt (r.lt₁ _ hf this) + +lemma identities_of_prop₂_of_eq {X Y : C} {f : X ⟶ Y} (hf : W₂ f) (h : r.deg X = r.deg Y) : + MorphismProperty.identities _ f := by + by_contra + exact h.not_lt (r.lt₂ _ hf this) + +include r in +lemma subsingleton_mapFactorizationData ⦃X Y : C⦄ (f : X ⟶ Y) : + Subsingleton (W₁.MapFactorizationData W₂ f) := by + have := (r.nonempty_unique f).some + infer_instance + +/-- The Reedy factorization of a morphism `f : X ⟶ Y` as a morphism in `W₁` +followed by a morphism in `W₂`. -/ +@[no_expose] +noncomputable def mapFactorizationData {X Y : C} (f : X ⟶ Y) : + W₁.MapFactorizationData W₂ f := by + letI := (r.nonempty_unique f).some + exact default + +include r in +lemma unique_obj {X Y : C} {f : X ⟶ Y} (fac fac' : W₁.MapFactorizationData W₂ f) : + fac.Z = fac'.Z := by + have := r.subsingleton_mapFactorizationData f + obtain rfl : fac = fac' := Subsingleton.elim _ _ + rfl + +include r in +lemma unique {X Y : C} {f : X ⟶ Y} (fac fac' : W₁.MapFactorizationData W₂ f) : + ∃ (h : fac.Z = fac'.Z), fac.i = fac'.i ≫ eqToHom h.symm ∧ fac.p = eqToHom h ≫ fac'.p := by + have := r.subsingleton_mapFactorizationData f + obtain rfl : fac = fac' := Subsingleton.elim _ _ + simp + +/-- The degree of a morphisms for a Reedy structure. It is defined as the degree of +the intermediate object in the Reedy factorization, but it is also the smallest +degree of an intermediate object in a factorization, see the lemma `degHom_le`. -/ +@[no_expose] +noncomputable def degHom {X Y : C} (f : X ⟶ Y) : α := r.deg (r.mapFactorizationData f).Z + +lemma degHom_eq {X Y : C} {f : X ⟶ Y} (h : W₁.MapFactorizationData W₂ f) : + r.degHom f = r.deg h.Z := by + have := r.subsingleton_mapFactorizationData + rw [← Subsingleton.elim (r.mapFactorizationData f) h] + rfl + +lemma exists_fac {X Y : C} (f : X ⟶ Y) : + ∃ (Z : C) (a : X ⟶ Z) (b : Z ⟶ Y), W₁ a ∧ W₂ b ∧ a ≫ b = f ∧ r.degHom f = r.deg Z := + ⟨_, _, _, (r.mapFactorizationData f).hi, (r.mapFactorizationData f).hp, + (r.mapFactorizationData f).fac, rfl⟩ + +lemma degHom_le {X Z Y : C} (f : X ⟶ Z) (g : Z ⟶ Y) : + r.degHom (f ≫ g) ≤ r.deg Z := by + obtain ⟨Zf, f₁, f₂, hf₁, hf₂, fac_f, eq_f⟩ := r.exists_fac f + obtain ⟨Zg, g₁, g₂, hg₁, hg₂, fac_g, eq_g⟩ := r.exists_fac g + obtain ⟨Zh, h₁, h₂, hh₁, hh₂, fac_h, eq_h⟩ := r.exists_fac (f₂ ≫ g₁) + let factfg := MorphismProperty.MapFactorizationData.mk (f := f ≫ g) Zh (f₁ ≫ h₁) (h₂ ≫ g₂) + (by simp [reassoc_of% fac_h, reassoc_of% fac_f, fac_g]) + (W₁.comp_mem _ _ hf₁ hh₁) (W₂.comp_mem _ _ hh₂ hg₂) + rw [r.degHom_eq factfg] + exact (r.le₁ _ hh₁).trans (r.le₂ _ hf₂) + +lemma degHom_le_deg_left {X Y : C} (f : X ⟶ Y) : + r.degHom f ≤ r.deg X := by + simpa using r.degHom_le (𝟙 X) f + +lemma degHom_le_deg_right {X Y : C} (f : X ⟶ Y) : + r.degHom f ≤ r.deg Y := by + simpa using r.degHom_le f (𝟙 Y) + +lemma degHom_comp_le_left {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) : + r.degHom (f ≫ g) ≤ r.degHom f := by + have ⟨_, f₁, f₂, _, _, h_fac, h_deg⟩ := r.exists_fac f + rw [h_deg, ← h_fac, Category.assoc] + exact r.degHom_le f₁ (f₂ ≫ g) + +lemma degHom_comp_le_right {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) : + r.degHom (f ≫ g) ≤ r.degHom g := by + have ⟨_, g₁, g₂, _, _, h_fac, h_deg⟩ := r.exists_fac g + rw [h_deg, ← h_fac, <- Category.assoc] + exact r.degHom_le (f ≫ g₁) g₂ + +lemma prop₂_of_degHom_eq_deg_left {X Y : C} {f : X ⟶ Y} (hf : r.degHom f = r.deg X) : + W₂ f := by + obtain ⟨Z, p, i, hp, hi, fac, h⟩ := r.exists_fac f + obtain ⟨_⟩ := r.identities_of_prop₁_of_eq hp (by aesop) + obtain rfl : i = f := by simpa using fac + exact hi + +lemma prop₁_of_degHom_eq_deg_right {X Y : C} {f : X ⟶ Y} (hf : r.degHom f = r.deg Y) : + W₁ f := by + obtain ⟨Z, p, i, hp, hi, fac, h⟩ := r.exists_fac f + obtain ⟨_⟩ := r.identities_of_prop₂_of_eq hi (by aesop) + obtain rfl : p = f := by simpa using fac + exact hp + +lemma degHom_lt_or_of_degHom_comp_lt + {X Z Y : C} (f : X ⟶ Z) (g : Z ⟶ Y) (hfg : r.degHom (f ≫ g) < r.deg Z) : + r.degHom f < r.deg Z ∨ r.degHom g < r.deg Z := by + contrapose! hfg + let φ := MorphismProperty.MapFactorizationData.mk Z f g rfl + (r.prop₁_of_degHom_eq_deg_right (le_antisymm (r.degHom_le_deg_right f) hfg.left)) + (r.prop₂_of_degHom_eq_deg_left (le_antisymm (r.degHom_le_deg_left g) hfg.right)) + rw [r.degHom_eq φ] + +@[simp] +lemma degHom_id (X : C) : r.degHom (𝟙 X) = r.deg X := + r.degHom_eq (MorphismProperty.MapFactorizationData.mk X (𝟙 X) (𝟙 X) (by simp) (W₁.id_mem _) + (W₂.id_mem _)) + +lemma deg_eq_of_iso {X Y : C} (e : X ≅ Y) : r.deg X = r.deg Y := by + have {X Y : C} (e : X ≅ Y) : r.deg X ≤ r.deg Y := by + rw [← r.degHom_id X, ← e.hom_inv_id] + apply r.degHom_le + exact le_antisymm (this e) (this e.symm) + +include r in +lemma prop₁_of_iso {X Y : C} (e : X ≅ Y) : W₁ e.hom := + r.prop₁_of_degHom_eq_deg_right (by + refine le_antisymm ?_ ?_ + · simpa using r.degHom_comp_le_right e.hom (𝟙 Y) + · simpa using r.degHom_comp_le_right e.inv e.hom) + +include r in +lemma prop₂_of_iso {X Y : C} (e : X ≅ Y) : W₂ e.hom := + (r.op.prop₁_of_iso e.op) + +include r in +lemma skeletal : Skeletal C := by + intro X Y ⟨e⟩ + exact (r.unique (f := e.hom) + (.mk X (𝟙 X) e.hom (by simp) (W₁.id_mem X) (r.prop₂_of_iso e)) + (.mk Y e.hom (𝟙 Y) (by simp) (r.prop₁_of_iso e) (W₂.id_mem Y))).choose + +end ReedyStructure + +end HomotopicalAlgebra diff --git a/Mathlib/AlgebraicTopology/SimplexCategory/GeneratorsRelations/NormalForms.lean b/Mathlib/AlgebraicTopology/SimplexCategory/GeneratorsRelations/NormalForms.lean index ca7e85ec11cc33..284959334420aa 100644 --- a/Mathlib/AlgebraicTopology/SimplexCategory/GeneratorsRelations/NormalForms.lean +++ b/Mathlib/AlgebraicTopology/SimplexCategory/GeneratorsRelations/NormalForms.lean @@ -124,8 +124,8 @@ lemma head_lt {m a L} (hL : IsAdmissible m (a :: L)) : ∀ a' ∈ L, a < a' := fun _ => L.rel_of_pairwise_cons hL.sortedLT.pairwise @[grind →] lemma getElem_lt {m L} (hL : IsAdmissible m L) - {k : ℕ} {hk : k < L.length} : L[k] < m + L.length := - (hL.le k hk).trans_lt (Nat.add_lt_add_left hk _) + {k : ℕ} {hk : k < L.length} : L[k] < m + L.length := by + grw [hL.le, hk] /-- An element of an `m`-admissible list, as an element of the appropriate `Fin` -/ @[simps] diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/PushoutProduct.lean b/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/PushoutProduct.lean index 4288b72d875f09..8816b313836957 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/PushoutProduct.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/Inner/PushoutProduct.lean @@ -110,44 +110,38 @@ lemma innerAnodyneExtensions_pushoutObjObjι' end -set_option backward.defeqAttrib.useBackward true in lemma innerAnodyneExtensions_unionProd_ι {X Y : SSet.{u}} (A : X.Subcomplex) (B : Y.Subcomplex) (hB : innerAnodyneExtensions B.ι) : - innerAnodyneExtensions (A.unionProd B).ι := by - simpa using innerAnodyneExtensions_pushoutObjObjι (Subcomplex.unionProd.pushoutObjObj A B) hB + innerAnodyneExtensions (A.unionProd B).ι := + innerAnodyneExtensions_pushoutObjObjι (Subcomplex.unionProd.pushoutObjObj A B) hB -set_option backward.defeqAttrib.useBackward true in lemma innerAnodyneExtensions_unionProd_ι' {X Y : SSet.{u}} (A : X.Subcomplex) (B : Y.Subcomplex) (hA : innerAnodyneExtensions A.ι) : - innerAnodyneExtensions (A.unionProd B).ι := by - simpa using innerAnodyneExtensions_pushoutObjObjι' (Subcomplex.unionProd.pushoutObjObj A B) hA + innerAnodyneExtensions (A.unionProd B).ι := + innerAnodyneExtensions_pushoutObjObjι' (Subcomplex.unionProd.pushoutObjObj A B) hA -set_option backward.defeqAttrib.useBackward true in lemma innerAnodyneExtensions.whiskerRight {X Y : SSet.{u}} {f : X ⟶ Y} (hf : innerAnodyneExtensions f) (Z : SSet.{u}) : - innerAnodyneExtensions (f ▷ Z) := by - simpa using innerAnodyneExtensions_pushoutObjObjι' + innerAnodyneExtensions (f ▷ Z) := + innerAnodyneExtensions_pushoutObjObjι' (.ofIsInitialRight (curriedTensor _) f (initial.to Z) initialIsInitial) hf -set_option backward.defeqAttrib.useBackward true in lemma innerAnodyneExtensions.whiskerLeft {X Y : SSet.{u}} {f : X ⟶ Y} (hf : innerAnodyneExtensions f) (Z : SSet.{u}) : - innerAnodyneExtensions (Z ◁ f) := by - simpa using innerAnodyneExtensions_pushoutObjObjι + innerAnodyneExtensions (Z ◁ f) := + innerAnodyneExtensions_pushoutObjObjι (.ofIsInitialLeft (curriedTensor _) (initial.to Z) f initialIsInitial) hf -set_option backward.defeqAttrib.useBackward true in instance {E B X : SSet.{u}} (p : E ⟶ B) [InnerFibration p] : - InnerFibration ((ihom X).map p) := by - simpa using innerFibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsInitial + InnerFibration ((ihom X).map p) := + innerFibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsInitial MonoidalClosed.internalHom (initial.to X) p initialIsInitial) -set_option backward.isDefEq.respectTransparency false in instance {A B : SSet.{u}} (i : A ⟶ B) [Mono i] (X : SSet.{u}) [Quasicategory X] : - InnerFibration ((MonoidalClosed.pre i).app X) := by - simpa using innerFibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsTerminal + InnerFibration ((MonoidalClosed.pre i).app X) := + innerFibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsTerminal MonoidalClosed.internalHom i (terminal.from X) terminalIsTerminal) instance (A : SSet.{u}) : Quasicategory ((ihom A).obj (⊤_ _)) := by diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/PushoutProduct.lean b/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/PushoutProduct.lean index 9155e285cd79c1..b043fc8c529dbc 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/PushoutProduct.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/AnodyneExtensions/PushoutProduct.lean @@ -112,44 +112,38 @@ lemma anodyneExtensions_pushoutObjObjι' end -set_option backward.defeqAttrib.useBackward true in lemma anodyneExtensions_unionProd_ι {X Y : SSet.{u}} (A : X.Subcomplex) (B : Y.Subcomplex) (hB : anodyneExtensions B.ι) : - anodyneExtensions (A.unionProd B).ι := by - simpa using anodyneExtensions_pushoutObjObjι (Subcomplex.unionProd.pushoutObjObj A B) hB + anodyneExtensions (A.unionProd B).ι := + anodyneExtensions_pushoutObjObjι (Subcomplex.unionProd.pushoutObjObj A B) hB -set_option backward.defeqAttrib.useBackward true in lemma anodyneExtensions_unionProd_ι' {X Y : SSet.{u}} (A : X.Subcomplex) (B : Y.Subcomplex) (hA : anodyneExtensions A.ι) : - anodyneExtensions (A.unionProd B).ι := by - simpa using anodyneExtensions_pushoutObjObjι' (Subcomplex.unionProd.pushoutObjObj A B) hA + anodyneExtensions (A.unionProd B).ι := + anodyneExtensions_pushoutObjObjι' (Subcomplex.unionProd.pushoutObjObj A B) hA -set_option backward.defeqAttrib.useBackward true in lemma anodyneExtensions.whiskerRight {X Y : SSet.{u}} {f : X ⟶ Y} (hf : anodyneExtensions f) (Z : SSet.{u}) : - anodyneExtensions (f ▷ Z) := by - simpa using anodyneExtensions_pushoutObjObjι' + anodyneExtensions (f ▷ Z) := + anodyneExtensions_pushoutObjObjι' (.ofIsInitialRight (curriedTensor _) f (initial.to Z) initialIsInitial) hf -set_option backward.defeqAttrib.useBackward true in lemma anodyneExtensions.whiskerLeft {X Y : SSet.{u}} {f : X ⟶ Y} (hf : anodyneExtensions f) (Z : SSet.{u}) : - anodyneExtensions (Z ◁ f) := by - simpa using anodyneExtensions_pushoutObjObjι + anodyneExtensions (Z ◁ f) := + anodyneExtensions_pushoutObjObjι (.ofIsInitialLeft (curriedTensor _) (initial.to Z) f initialIsInitial) hf -set_option backward.defeqAttrib.useBackward true in instance {E B X : SSet.{u}} (p : E ⟶ B) [Fibration p] : - Fibration ((ihom X).map p) := by - simpa using fibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsInitial + Fibration ((ihom X).map p) := + fibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsInitial MonoidalClosed.internalHom (initial.to X) p initialIsInitial) -set_option backward.isDefEq.respectTransparency false in instance {A B : SSet.{u}} (i : A ⟶ B) [Mono i] (X : SSet.{u}) [KanComplex X] : - Fibration ((MonoidalClosed.pre i).app X) := by - simpa using fibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsTerminal + Fibration ((MonoidalClosed.pre i).app X) := + fibration_pullbackObjObjπ (Functor.PullbackObjObj.ofIsTerminal MonoidalClosed.internalHom i (terminal.from X) terminalIsTerminal) instance (A : SSet.{u}) : KanComplex ((ihom A).obj (⊤_ _)) := by diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean b/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean index 12e7737e704c5a..f2c473ade6dfe4 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean @@ -29,7 +29,7 @@ variable (X : SSet.{u}) /-- If `X : SSet`, this is the functor `X.N ⥤ SSet` which sends a nondegenerate simplex of `X` to the subcomplex of `X` that it generates. -/ -@[expose, simps! obj] +@[expose, simps! obj map] public def functorN : X.N ⥤ SSet.{u} := X.orderEmbeddingN.monotone.functor ⋙ Subcomplex.toSSetFunctor diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean b/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean index 50bb14b9e13fba..0d31bdaaec145b 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/Nonsingular.lean @@ -32,7 +32,7 @@ public section universe u -open CategoryTheory MonoidalCategory Simplicial +open CategoryTheory MonoidalCategory Simplicial Opposite namespace SSet @@ -104,4 +104,79 @@ lemma Nonsingular.δ_injective [X.Nonsingular] have := mono' x hx exact injective_of_mono ((yonedaEquiv.symm x).app _) hij +lemma Nonsingular.injective_map + [X.Nonsingular] {n : ℕ} (x : X _⦋n⦌) (hx : x ∈ X.nonDegenerate n) + {m : SimplexCategory} {f g : m ⟶ ⦋n⦌} + (h : X.map f.op x = X.map g.op x) : + f = g := by + have := Nonsingular.mono' x hx + apply stdSimplex.{u}.map_injective + rw [← cancel_mono (yonedaEquiv.symm x)] + apply yonedaEquiv.injective + simpa [yonedaEquiv_comp, yonedaEquiv_map] + +lemma Nonsingular.isIso_toOfSimplex [X.Nonsingular] + {n : ℕ} (x : X _⦋n⦌) (hx : x ∈ X.nonDegenerate n) : + IsIso (Subcomplex.toOfSimplex x) := by + rw [Subcomplex.isIso_toOfSimplex_iff] + exact Nonsingular.mono' x hx + +/-- If `x : X _⦋n⦌` is a nondegenerate simplex of a nonsingular simplcial set, +this is the isomorphism `Δ[n] ≅ Subcomplex.ofSimplex x` induced by `x`. -/ +@[expose, simps! hom] +noncomputable def Nonsingular.iso + [X.Nonsingular] {n : ℕ} (x : X _⦋n⦌) (hx : x ∈ X.nonDegenerate n) : + Δ[n] ≅ Subcomplex.ofSimplex x := + letI := Nonsingular.isIso_toOfSimplex x hx + asIso (Subcomplex.toOfSimplex x) + +namespace N + +variable [X.Nonsingular] {x y z : X.N} (h : x ≤ y) + +include h in +lemma existsUnique_of_le : + ∃! (f : ⦋x.dim⦌ ⟶ ⦋y.dim⦌), Mono f ∧ X.map f.op y.1.2 = x.1.2 := + existsUnique_of_exists_of_unique (by + obtain ⟨f, _, hf⟩ := le_iff_exists_mono.1 h + exact ⟨f, inferInstance, hf⟩) (fun f₁ f₂ ⟨_, hf₁⟩ ⟨_, hf₂⟩ ↦ by + exact Nonsingular.injective_map _ y.nonDegenerate (by rw [hf₁, hf₂])) + +/-- Given an inequality `x ≤ y` between nondegenerate simplices of a +nonsingular simplicial set `X`, this is the corresponding morphism +`⦋x.dim⦌ ⟶ ⦋y.dim⦌` in the simplex category. -/ +noncomputable def monoOfLE : ⦋x.dim⦌ ⟶ ⦋y.dim⦌ := + (existsUnique_of_le h).exists.choose + +instance : Mono (monoOfLE h) := + (existsUnique_of_le h).exists.choose_spec.1 + +@[simp] +lemma map_monoOfLE : X.map (monoOfLE h).op y.simplex = x.simplex := + (existsUnique_of_le h).exists.choose_spec.2 + +@[reassoc, simp] +lemma stdSimplex_map_monoOfLE_yonedaEquiv_symm_simplex : + stdSimplex.map (monoOfLE h) ≫ yonedaEquiv.symm y.simplex = + yonedaEquiv.symm x.simplex := by + rw [yonedaEquiv_symm_naturality_left, map_monoOfLE] + +lemma monoOfLE_eq_iff (h : x ≤ y) (g : ⦋x.dim⦌ ⟶ ⦋y.dim⦌) [Mono g] : + monoOfLE h = g ↔ X.map g.op y.simplex = x.simplex := + ⟨by rintro rfl; simp, + fun h' ↦ (existsUnique_of_le h).unique ⟨inferInstance, by simp⟩ ⟨inferInstance, h'⟩⟩ + +variable (x) in +@[simp] +lemma monoOfLE_refl : monoOfLE (le_refl x) = 𝟙 _ := by + simp [monoOfLE_eq_iff] + +@[reassoc (attr := simp)] +lemma monoOfLE_comp (h' : y ≤ z) : + monoOfLE h ≫ monoOfLE h' = monoOfLE (h.trans h') := by + symm + simp [monoOfLE_eq_iff] + +end N + end SSet diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/NonsingularColimit.lean b/Mathlib/AlgebraicTopology/SimplicialSet/NonsingularColimit.lean new file mode 100644 index 00000000000000..15635797ca2175 --- /dev/null +++ b/Mathlib/AlgebraicTopology/SimplicialSet/NonsingularColimit.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.AlgebraicTopology.SimplexCategory.SemiSimplexCategory +public import Mathlib.AlgebraicTopology.SimplicialSet.Nonsingular +public import Mathlib.AlgebraicTopology.SimplicialSet.NonDegenerateSimplicesColimit + +/-! +# Nonsingular simplicial sets, as colimits of standard simplices + +In the file `Mathlib/AlgebraicTopology/SimplicialSet/NonDegenerateSimplicesColimit.lean`, +it was shown that any simplicial set `X` is the colimit (indexed by the type `X.N` +of nondegenerate simplices) of its monogenous subcomplexes. + +In this file, we assume that `X` is nonsingular, in which case its monogenous subcomplexes +identify to standard simplices. This allows to show that `X` is the colimit +of `Δ[x.dim]` for `x : X.N`. + +-/ + +@[expose] public section + +universe u + +open CategoryTheory Simplicial Limits + +namespace SSet + +variable (X : SSet.{u}) [X.Nonsingular] + +namespace N + +set_option backward.isDefEq.respectTransparency false in +/-- If `X` is a nonsingular simplicial set, this is the functor +`X.N ⥤ SemiSimplexCategory` which sends a nondegenerate +simplex `s : X : N` to `⦋s.dim⦌ₛ` -/ +@[simps obj map] +noncomputable def toSemiSimplexCategory : X.N ⥤ SemiSimplexCategory where + obj s := ⦋s.dim⦌ₛ + map f := SemiSimplexCategory.homOfMono (N.monoOfLE (leOfHom f)) + map_id _ := SemiSimplexCategory.toSimplexCategory.map_injective (by simp) + map_comp _ _ := SemiSimplexCategory.toSimplexCategory.map_injective (by simp) + +end N + +/-- The functor `X.N ⥤ SSet` which sends `x : X.N` to `Δ[x.dim]`. -/ +noncomputable abbrev functorN' : X.N ⥤ SSet.{u} := + N.toSemiSimplexCategory X ⋙ SemiSimplexCategory.toSimplexCategory ⋙ SSet.stdSimplex + +set_option backward.defeqAttrib.useBackward true in +/-- The isomorphism `X.functorN' ≅ X.functorN` for a nonsingular simplicial set `X`. -/ +noncomputable def functorN'Iso : X.functorN' ≅ X.functorN := + NatIso.ofComponents (fun x ↦ Nonsingular.iso _ x.nonDegenerate) (fun _ ↦ by + simp [← cancel_mono (Subcomplex.ι _)]) + +/-- If `X` is a nonsingular simplicial set, this is the cocone consisting +of the (mono)morphisms `Δ[x.dim] ⟶ X` for all nondegenerate simplices `x : X.N`. -/ +@[simps] +noncomputable def coconeN' : Cocone X.functorN' where + pt := X + ι.app s := yonedaEquiv.symm s.simplex + ι.naturality _ _ f := N.stdSimplex_map_monoOfLE_yonedaEquiv_symm_simplex (leOfHom f) + +/-- If `X` is a nonsingular simplicial set, `X` is the colimit of `Δ[x.dim]` +for all nondegenerate simplices `x : X.N`. -/ +noncomputable def isColimitCoconeN' : IsColimit X.coconeN' := + (IsColimit.equivOfNatIsoOfIso + X.functorN'Iso.symm _ _ (Cocone.ext (Iso.refl _))).1 X.isColimitCoconeN + +end SSet diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/PushoutProduct.lean b/Mathlib/AlgebraicTopology/SimplicialSet/PushoutProduct.lean index a37287f012af70..fa376a2784977d 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/PushoutProduct.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/PushoutProduct.lean @@ -36,8 +36,9 @@ set_option backward.defeqAttrib.useBackward true in noncomputable def ιIso : Arrow.mk (S.unionProd T).ι ≅ S.ι □ T.ι := Arrow.isoMk' _ _ (isPushout S T).isoPushout (Iso.refl _) - (by apply (unionProd.isPushout S T).hom_ext <;> - simp [Functor.PushoutObjObj.ofHasPushout, Functor.PushoutObjObj.ι]) + (by + apply (unionProd.isPushout S T).hom_ext <;> + simp [Limits.pushout.inl_desc, Limits.pushout.inr_desc]) /-- Given subcomplexes `S` and `T` of simplicial sets, this if a `Functor.PushoutObjObj` structure for the chosen binary products on `SSet`, with point `S.unionProd T`. -/ @@ -47,15 +48,7 @@ noncomputable def pushoutObjObj : (curriedTensor _).PushoutObjObj S.ι T.ι wher inl := unionProd.ι₁ S T inr := unionProd.ι₂ S T isPushout := unionProd.isPushout S T - -set_option backward.defeqAttrib.useBackward true in -@[simp] -lemma pushoutObjObj_ι : (pushoutObjObj S T).ι = (S.unionProd T).ι := by - apply (pushoutObjObj S T).hom_ext - · rw [(pushoutObjObj S T).inl_ι] - simp - · rw [(pushoutObjObj S T).inr_ι] - simp + ι := (S.unionProd T).ι end unionProd diff --git a/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean b/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean index b0448158cbc584..2e06c56fee1cb4 100644 --- a/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean +++ b/Mathlib/AlgebraicTopology/SimplicialSet/StdSimplex.lean @@ -50,6 +50,13 @@ namespace stdSimplex open Finset Opposite SimplexCategory +/-- The functor `stdSimplex : SimplexCategory ⥤ SSet` is fully faithful; -/ +abbrev fullyFaithful : stdSimplex.{u}.FullyFaithful := + ULiftYoneda.fullyFaithful SimplexCategory + +instance : stdSimplex.{u}.Full := fullyFaithful.full +instance : stdSimplex.{u}.Faithful := fullyFaithful.faithful + @[simp] lemma map_id (n : SimplexCategory) : (SSet.stdSimplex.map (SimplexCategory.Hom.mk OrderHom.id : n ⟶ n)) = 𝟙 _ := @@ -302,6 +309,17 @@ lemma yonedaEquiv_symm_app_id {X : SSet.{u}} {n : ℕ} (x : X _⦋n⦌) : (yonedaEquiv.symm x).app _ (yonedaEquiv (𝟙 _)) = x := by simp +lemma yonedaEquiv_naturality {X : SSet} {m n : SimplexCategory} + (f : m ⟶ n) (g : stdSimplex.obj n ⟶ X) : + X.map f.op (yonedaEquiv g) = yonedaEquiv (stdSimplex.map f ≫ g) := + uliftYonedaEquiv_naturality _ _ + +@[reassoc] +lemma yonedaEquiv_symm_naturality_left {X : SSet} {m n : SimplexCategory} + (f : m ⟶ n) (g : X.obj (Opposite.op n)) : + stdSimplex.map f ≫ yonedaEquiv.symm g = yonedaEquiv.symm (X.map f.op g) := by + rw [← yonedaEquiv.apply_eq_iff_eq_symm_apply, ← yonedaEquiv_naturality, + yonedaEquiv.apply_symm_apply] namespace Subcomplex @@ -607,12 +625,10 @@ identify to subsets of `Fin (n + 1)` of cardinality `d + 1`. -/ (Δ[n] : SSet.{u}).nonDegenerate d ≃ { S : Finset (Fin (n + 1)) | S.card = d + 1 } := Equiv.ofBijective _ (bijective_image_objEquiv_toOrderHom_univ n d) -set_option backward.isDefEq.respectTransparency false in lemma nonDegenerateEquiv'_iff {n d : ℕ} (x : (Δ[n] : SSet.{u}).nonDegenerate d) (j : Fin (n + 1)) : j ∈ (nonDegenerateEquiv' x).val ↔ ∃ (i : Fin (d + 1)), x.val i = j := by - simp only [Set.mem_setOf_eq, Set.coe_setOf] - dsimp [nonDegenerateEquiv'] - aesop + unfold nonDegenerateEquiv' + simp set_option backward.defeqAttrib.useBackward true in /-- If `x` is a nondegenerate `d`-simplex of `Δ[n]`, this is the order isomorphism @@ -784,4 +800,46 @@ noncomputable def stdSimplex : SimplexCategory ⥤ SSet.Augmented.{u} where end Augmented +namespace Subcomplex + +variable {X : SSet.{u}} {n : ℕ} (x : X _⦋n⦌) + +/-- Given `x : X _⦋n⦌`, this is the epimorphism from `Δ[n]` +to the subcomplex of `X` generated by `x`. -/ +def toOfSimplex : Δ[n] ⟶ ofSimplex x := + Subcomplex.lift (yonedaEquiv.symm x) (by simp [range_eq_ofSimplex]) + +@[reassoc (attr := simp)] +lemma toOfSimplex_ι : + toOfSimplex x ≫ (ofSimplex x).ι = yonedaEquiv.symm x := rfl + +@[simp] +lemma yonedaEquiv_toOfSimplex : + yonedaEquiv (toOfSimplex x) = ⟨x, mem_ofSimplex_obj x⟩ := + yonedaEquiv.symm.injective (by cat_disch) + +set_option backward.defeqAttrib.useBackward true in +instance : Epi (toOfSimplex x) := by + rw [← range_eq_top_iff] + ext m ⟨_, u, rfl⟩ + simp only [range_eq_ofSimplex, yonedaEquiv_toOfSimplex, Subfunctor.top_obj, + Set.top_eq_univ, Set.mem_univ, iff_true] + refine ⟨u, ?_⟩ + dsimp + ext + rw [← yonedaEquiv.right_inv x] + aesop + +lemma isIso_toOfSimplex_iff : + IsIso (toOfSimplex x) ↔ Mono (yonedaEquiv.symm x) := by + constructor + · intro + rw [← toOfSimplex_ι] + infer_instance + · intro h + have := mono_of_mono_fac (toOfSimplex_ι x) + apply isIso_of_mono_of_epi + +end Subcomplex + end SSet diff --git a/Mathlib/Analysis/AbsoluteValue/Equivalence.lean b/Mathlib/Analysis/AbsoluteValue/Equivalence.lean index a74d5ea5364682..065f7dcf02d4f4 100644 --- a/Mathlib/Analysis/AbsoluteValue/Equivalence.lean +++ b/Mathlib/Analysis/AbsoluteValue/Equivalence.lean @@ -389,4 +389,51 @@ theorem isEquiv_iff_isHomeomorph (v w : AbsoluteValue F ℝ) : end Real +section WeakApproximation + +open Filter +open scoped Topology + +variable {F : Type*} [Field F] + +/-- +If `v : ι → AbsoluteValue F ℝ` is a finite family of nontrivial, pairwise inequivalent +real absolute values on a field `F`, then the diagonal embedding +`algebraMap F ((i : ι) → WithAbs (v i))` has dense range. + +This is the abstract weak approximation theorem; see +`NumberField.InfinitePlace.denseRange_algebraMap_pi` for the number-field special case. +-/ +theorem denseRange_algebraMap_pi {ι : Type*} [Finite ι] {v : ι → AbsoluteValue F ℝ} + (h : ∀ i, (v i).IsNontrivial) + (hv : Pairwise fun i j ↦ ¬(v i).IsEquiv (v j)) : + DenseRange <| algebraMap F ((i : ι) → WithAbs (v i)) := by + classical + have := Fintype.ofFinite ι + refine Metric.denseRange_iff.mpr fun z r hr ↦ ?_ + choose a hx using exists_one_lt_lt_one_pi_of_not_isEquiv h hv + let y := fun n : ℕ ↦ ∑ i, (1 / (1 + (a i)⁻¹ ^ n)) * WithAbs.equiv (v i) (z i) + have htend : atTop.Tendsto (fun n i ↦ (WithAbs.equiv (v i)).symm (y n)) (𝓝 z) := by + refine tendsto_pi_nhds.mpr fun u ↦ ?_ + simp_rw [← Fintype.sum_pi_single u z, y, map_sum, map_mul] + refine tendsto_finsetSum _ fun w _ ↦ ?_ + by_cases hw : u = w + · rw [← hw, Pi.single_eq_same] + have hlt : (v u) (a u)⁻¹ < 1 := by + simpa [← inv_pow, inv_lt_one_iff₀] using Or.inr (hx u).1 + simpa using (WithAbs.tendsto_one_div_one_add_pow_nhds_one hlt).mul_const (z u) + · rw [Pi.single_eq_of_ne (M := fun i ↦ WithAbs (v i)) hw (z w)] + have hgt : 1 < (v u) (a w)⁻¹ := by + rw [map_inv₀] + refine one_lt_inv_iff₀.mpr ⟨(v u).pos_iff.mpr fun ha ↦ ?_, (hx w).2 u hw⟩ + linarith [map_zero (v w) ▸ ha ▸ (hx w).1] + have := (v u).tendsto_div_one_add_pow_nhds_zero hgt + simp_rw [← WithAbs.norm_toAbs_eq] at this + simpa using (tendsto_zero_iff_norm_tendsto_zero.2 this).mul_const + ((WithAbs.equiv (v u)).symm _) + let ⟨N, hN⟩ := Metric.tendsto_atTop.1 htend r hr + exact ⟨y N, dist_comm z (algebraMap F _ (y N)) ▸ hN N le_rfl⟩ + +end WeakApproximation + end AbsoluteValue diff --git a/Mathlib/Analysis/Analytic/Binomial.lean b/Mathlib/Analysis/Analytic/Binomial.lean index e86e83ba680519..d323470efb0c04 100644 --- a/Mathlib/Analysis/Analytic/Binomial.lean +++ b/Mathlib/Analysis/Analytic/Binomial.lean @@ -186,7 +186,7 @@ theorem one_div_sub_pow_hasFPowerSeriesOnBall_zero (a : ℕ) {z : ℂ} (hz : z simp only [one_div, FunLike.coe_smul, H, Function.comp_def] at this convert (this.const_smul (c := (z ^ (a + 1))⁻¹)).congr ?_ · ext n - simp only [FormalMultilinearSeries.smul_apply, ContinuousMultilinearMap.smul_apply, + simp only [FormalMultilinearSeries.smul_apply, smul_apply, FormalMultilinearSeries.compContinuousLinearMap_apply] simp [add_assoc, pow_add _ _ (a + 1), mul_assoc] · intro w hw diff --git a/Mathlib/Analysis/Analytic/CPolynomialDef.lean b/Mathlib/Analysis/Analytic/CPolynomialDef.lean index 59c66e2d8114ec..080fd7c388245b 100644 --- a/Mathlib/Analysis/Analytic/CPolynomialDef.lean +++ b/Mathlib/Analysis/Analytic/CPolynomialDef.lean @@ -215,7 +215,7 @@ theorem HasFiniteFPowerSeriesOnBall.eq_partialSum fun y hy m hm ↦ (hf.hasSum hy).unique (hasSum_sum_of_ne_finset_zero (f := fun m => p m (fun _ => y)) (s := Finset.range m) (fun N hN => by simp only [Finset.mem_range, not_lt] at hN - rw [hf.finite _ (le_trans hm hN), ContinuousMultilinearMap.zero_apply])) + rw [hf.finite _ (le_trans hm hN), zero_apply])) /-- Variant of the previous result with the variable expressed as `y` instead of `x + y`. -/ theorem HasFiniteFPowerSeriesOnBall.eq_partialSum' @@ -244,7 +244,7 @@ theorem HasFiniteFPowerSeriesOnBall.bound_zero_of_eq_zero (hf : ∀ y ∈ Metric · intro y hy rw [hf (x + y)] · convert! hasSum_zero - rw [hp, ContinuousMultilinearMap.zero_apply] + rw [hp, zero_apply] · rwa [Metric.mem_eball, edist_eq_enorm_sub, add_comm, add_sub_cancel_right, ← edist_zero_right, ← Metric.mem_eball] @@ -362,7 +362,7 @@ lemma changeOriginSeries_sum_eq_partialSum_of_finite (p : FormalMultilinearSerie intro m hm rw [Finset.mem_range, not_lt] at hm rw [p.changeOriginSeries_finite_of_finite hn k (by rw [add_comm]; exact Nat.le_add_of_sub_le hm), - ContinuousMultilinearMap.zero_apply] + _root_.zero_apply] /-- If `p` is a formal multilinear series such that `p m = 0` for `n ≤ m`, then `p.changeOrigin x k = 0` for `n ≤ k`. -/ @@ -373,8 +373,7 @@ lemma changeOrigin_finite_of_finite (p : FormalMultilinearSeries 𝕜 E F) {n : apply Finset.sum_eq_zero intro m hm rw [Finset.mem_range] at hm - rw [p.changeOriginSeries_finite_of_finite hn k (le_add_of_le_left hk), - ContinuousMultilinearMap.zero_apply] + rw [p.changeOriginSeries_finite_of_finite hn k (le_add_of_le_left hk), _root_.zero_apply] theorem hasFiniteFPowerSeriesOnBall_changeOrigin (p : FormalMultilinearSeries 𝕜 E F) {n : ℕ} (k : ℕ) (hn : ∀ (m : ℕ), n + k ≤ m → p m = 0) : @@ -395,20 +394,18 @@ theorem changeOrigin_eval_of_finite (p : FormalMultilinearSeries 𝕜 E F) {n : · refine fun s ↦ Not.imp_symm fun hs ↦ ?_ simp only [preimage_setOf_eq, changeOriginIndexEquiv_apply_fst, mem_setOf, not_lt] at hs dsimp only [f] - rw [changeOriginSeriesTerm_bound p hn _ _ _ hs, ContinuousMultilinearMap.zero_apply, - ContinuousMultilinearMap.zero_apply] + rw [changeOriginSeriesTerm_bound p hn _ _ _ hs, _root_.zero_apply, _root_.zero_apply] have hfkl k l : HasSum (f ⟨k, l, ·⟩) (changeOriginSeries p k l (fun _ ↦ x) fun _ ↦ y) := by - simp_rw [changeOriginSeries, ContinuousMultilinearMap.sum_apply]; apply hasSum_fintype + simp_rw [changeOriginSeries, sum_apply]; apply hasSum_fintype have hfk k : HasSum (f ⟨k, ·⟩) (changeOrigin p x k fun _ ↦ y) := by have (m) (hm : m ∉ Finset.range n) : changeOriginSeries p k m (fun _ ↦ x) = 0 := by rw [Finset.mem_range, not_lt] at hm - rw [changeOriginSeries_finite_of_finite _ hn _ (le_add_of_le_right hm), - ContinuousMultilinearMap.zero_apply] + rw [changeOriginSeries_finite_of_finite _ hn _ (le_add_of_le_right hm), _root_.zero_apply] rw [changeOrigin, FormalMultilinearSeries.sum, ContinuousMultilinearMap.tsum_eval (summable_of_ne_finset_zero this)] refine (summable_of_ne_finset_zero (s := Finset.range n) fun m hm ↦ ?_).hasSum.sigma_of_hasSum (hfkl k) (summable_of_hasFiniteSupport <| finsupp.preimage sigma_mk_injective.injOn) - rw [this m hm, ContinuousMultilinearMap.zero_apply] + rw [this m hm, _root_.zero_apply] have hf : HasSum f ((p.changeOrigin x).sum y) := ((p.changeOrigin x).hasSum_of_finite (fun _ ↦ changeOrigin_finite_of_finite p hn) _) |>.sigma_of_hasSum hfk (summable_of_hasFiniteSupport finsupp) diff --git a/Mathlib/Analysis/Analytic/ChangeOrigin.lean b/Mathlib/Analysis/Analytic/ChangeOrigin.lean index 40a765dab824cf..3dbdbdc6ca2e91 100644 --- a/Mathlib/Analysis/Analytic/ChangeOrigin.lean +++ b/Mathlib/Analysis/Analytic/ChangeOrigin.lean @@ -279,7 +279,7 @@ theorem changeOrigin_eval (h : (‖x‖₊ + ‖y‖₊ : ℝ≥0∞) < p.radius have := (p.hasFPowerSeriesOnBall_changeOrigin k h.pos).hasSum x_mem_ball rw [zero_add] at this refine HasSum.sigma_of_hasSum this (fun l => ?_) ?_ - · simp only [changeOriginSeries, ContinuousMultilinearMap.sum_apply] + · simp only [changeOriginSeries, sum_apply] apply hasSum_fintype · refine .of_nnnorm_bounded (p.changeOriginSeries_summable_aux₂ (mem_eball_zero_iff.1 x_mem_ball) k) diff --git a/Mathlib/Analysis/Analytic/Composition.lean b/Mathlib/Analysis/Analytic/Composition.lean index a1ec939506e5f5..a2d67c703fbb11 100644 --- a/Mathlib/Analysis/Analytic/Composition.lean +++ b/Mathlib/Analysis/Analytic/Composition.lean @@ -277,7 +277,7 @@ theorem removeZero_comp_of_pos (q : FormalMultilinearSeries 𝕜 F G) q.removeZero.comp p n = q.comp p n := by ext v simp only [FormalMultilinearSeries.comp, compAlongComposition, - ContinuousMultilinearMap.compAlongComposition_apply, ContinuousMultilinearMap.sum_apply] + ContinuousMultilinearMap.compAlongComposition_apply, sum_apply] refine Finset.sum_congr rfl fun c _hc => ?_ rw [removeZero_of_pos _ (c.length_pos_of_pos hn)] @@ -395,10 +395,10 @@ theorem comp_id (p : FormalMultilinearSeries 𝕜 E F) (x : E) : p.comp (id 𝕜 let j : Fin b.length := ⟨i.val, b.blocks_length ▸ i.prop⟩ have A : 1 < b.blocksFun j := by convert! lt_k ext v - rw [compAlongComposition_apply, ContinuousMultilinearMap.zero_apply] + rw [compAlongComposition_apply, _root_.zero_apply] apply ContinuousMultilinearMap.map_coord_zero _ j dsimp [applyComposition] - rw [id_apply_of_one_lt _ _ _ A, ContinuousMultilinearMap.zero_apply] + rw [id_apply_of_one_lt _ _ _ A, _root_.zero_apply] · simp @[simp] @@ -427,8 +427,8 @@ theorem id_comp (p : FormalMultilinearSeries 𝕜 E F) (v0 : Fin 0 → E) : have : 0 < b.length := Composition.length_pos_of_pos b n_pos lia ext v - rw [compAlongComposition_apply, id_apply_of_one_lt _ _ _ A, - ContinuousMultilinearMap.zero_apply, ContinuousMultilinearMap.zero_apply] + rw [compAlongComposition_apply, id_apply_of_one_lt _ _ _ A, _root_.zero_apply, + _root_.zero_apply] · simp /-- Variant of `id_comp` in which the zero coefficient is given by an equality hypothesis instead @@ -813,8 +813,7 @@ theorem HasFPowerSeriesWithinAt.comp {g : F → G} {f : E → F} {q : FormalMult have E : HasSum (fun n => (q.comp p) n fun _j => y) (g (f (x + y))) := by apply D.sigma intro n - simp only [compAlongComposition_apply, FormalMultilinearSeries.comp, - ContinuousMultilinearMap.sum_apply] + simp only [compAlongComposition_apply, FormalMultilinearSeries.comp, sum_apply] exact hasSum_fintype _ rw [Function.comp_apply] exact E @@ -910,7 +909,7 @@ theorem HasFiniteFPowerSeriesAt.comp {m n : ℕ} {g : F → G} {f : E → F} apply Finset.sum_eq_zero rintro c - ext v - simp only [compAlongComposition_apply, ContinuousMultilinearMap.zero_apply] + simp only [compAlongComposition_apply, _root_.zero_apply] rcases le_or_gt m c.length with hc | hc · simp [hg.finite _ hc] obtain ⟨j, hj⟩ : ∃ j, n ≤ c.blocksFun j := by @@ -1256,9 +1255,9 @@ theorem comp_assoc (r : FormalMultilinearSeries 𝕜 G H) (q : FormalMultilinear r c.1.length fun i : Fin c.1.length => q (c.2 i).length (applyComposition p (c.2 i) (v ∘ c.1.embedding i)) suffices ∑ c, f c = ∑ c, g c by - simpa +unfoldPartialApp only [FormalMultilinearSeries.comp, - ContinuousMultilinearMap.sum_apply, compAlongComposition_apply, Finset.sum_sigma', - applyComposition, ContinuousMultilinearMap.map_sum] + simpa +unfoldPartialApp only [FormalMultilinearSeries.comp, sum_apply, + compAlongComposition_apply, Finset.sum_sigma', applyComposition, + ContinuousMultilinearMap.map_sum] /- Now, we use `Composition.sigmaEquivSigmaPi n` to change variables in the second sum, and check that we get exactly the same sums. -/ rw [← (sigmaEquivSigmaPi n).sum_comp] diff --git a/Mathlib/Analysis/Analytic/Constructions.lean b/Mathlib/Analysis/Analytic/Constructions.lean index 8d7fa921173079..44c8d8cc59f201 100644 --- a/Mathlib/Analysis/Analytic/Constructions.lean +++ b/Mathlib/Analysis/Analytic/Constructions.lean @@ -716,6 +716,71 @@ lemma AnalyticOnNhd.zpow_nonneg {f : E → 𝕝} {s : Set E} {n : ℤ} (hf : Ana simp_rw [(Eq.symm (Int.toNat_of_nonneg hn) : n = OfNat.ofNat n.toNat), zpow_ofNat] apply pow hf +/-! +### Composition with a linear map +-/ + +section compContinuousLinearMap + +variable {u : E →L[𝕜] F} {f : F → G} {pf : FormalMultilinearSeries 𝕜 F G} {s : Set F} {x : E} + {r : ℝ≥0∞} + +theorem HasFPowerSeriesWithinOnBall.compContinuousLinearMap + (hf : HasFPowerSeriesWithinOnBall f pf s (u x) r) : + HasFPowerSeriesWithinOnBall (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x (r / ‖u‖ₑ) where + r_le := by + calc + _ ≤ pf.radius / ‖u‖ₑ := by + gcongr + exact hf.r_le + _ ≤ _ := pf.div_le_radius_compContinuousLinearMap _ + r_pos := by + simp only [ENNReal.div_pos_iff, ne_eq, enorm_ne_top, not_false_eq_true, and_true] + exact pos_iff_ne_zero.mp hf.r_pos + hasSum hy1 hy2 := by + convert! hf.hasSum _ _ + · simp + · simp only [Set.mem_insert_iff, add_eq_left, Set.mem_preimage, map_add] at hy1 ⊢ + rcases hy1 with (hy1 | hy1) <;> simp [hy1] + · simp only [Metric.eball, edist_zero_right, Set.mem_setOf_eq] at hy2 ⊢ + exact lt_of_le_of_lt (ContinuousLinearMap.le_opENorm _ _) (mul_lt_of_lt_div' hy2) + +theorem HasFPowerSeriesOnBall.compContinuousLinearMap (hf : HasFPowerSeriesOnBall f pf (u x) r) : + HasFPowerSeriesOnBall (f ∘ u) (pf.compContinuousLinearMap u) x (r / ‖u‖ₑ) := by + rw [← hasFPowerSeriesWithinOnBall_univ] at hf ⊢ + exact hf.compContinuousLinearMap + +theorem HasFPowerSeriesAt.compContinuousLinearMap (hf : HasFPowerSeriesAt f pf (u x)) : + HasFPowerSeriesAt (f ∘ u) (pf.compContinuousLinearMap u) x := + let ⟨r, hr⟩ := hf + ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩ + +theorem HasFPowerSeriesWithinAt.compContinuousLinearMap + (hf : HasFPowerSeriesWithinAt f pf s (u x)) : + HasFPowerSeriesWithinAt (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x := + let ⟨r, hr⟩ := hf + ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩ + +theorem AnalyticAt.compContinuousLinearMap (hf : AnalyticAt 𝕜 f (u x)) : + AnalyticAt 𝕜 (f ∘ u) x := + let ⟨p, hp⟩ := hf + ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩ + +theorem AnalyticAtWithin.compContinuousLinearMap (hf : AnalyticWithinAt 𝕜 f s (u x)) : + AnalyticWithinAt 𝕜 (f ∘ u) (u ⁻¹' s) x := + let ⟨p, hp⟩ := hf + ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩ + +theorem AnalyticOn.compContinuousLinearMap (hf : AnalyticOn 𝕜 f s) : + AnalyticOn 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx => + AnalyticAtWithin.compContinuousLinearMap (hf (u x) hx) + +theorem AnalyticOnNhd.compContinuousLinearMap (hf : AnalyticOnNhd 𝕜 f s) : + AnalyticOnNhd 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx => + AnalyticAt.compContinuousLinearMap (hf (u x) hx) + +end compContinuousLinearMap + /-! ### Restriction of scalars -/ @@ -822,6 +887,53 @@ lemma analyticAt_inverse_one_sub [HasSummableGeomSeries A] : AnalyticAt 𝕜 (fun x : A ↦ (1 - x)⁻¹ʳ) 0 := ⟨_, ⟨_, hasFPowerSeriesOnBall_inverse_one_sub 𝕜 A⟩⟩ +/-- The alternating geometric series `1 - x + x ^ 2 - ...` as a `FormalMultilinearSeries`. -/ +def alternatingGeometricSeries : FormalMultilinearSeries 𝕜 A A := + .ofScalars A fun n ↦ (-1 : 𝕜) ^ n + +lemma alternatingGeometricSeries_eq_formalMultilinearSeries_geometric_comp_neg : + alternatingGeometricSeries 𝕜 A = + (formalMultilinearSeries_geometric 𝕜 A).compContinuousLinearMap + (-ContinuousLinearMap.id 𝕜 A) := by + simp [formalMultilinearSeries_geometric_eq_ofScalars, alternatingGeometricSeries, + FormalMultilinearSeries.ofScalars_comp_neg_id] + +lemma alternatingGeometricSeries_apply_norm_le (n : ℕ) : + ‖alternatingGeometricSeries 𝕜 A n‖ ≤ max 1 ‖(1 : A)‖ := by + simpa [alternatingGeometricSeries] using + ContinuousMultilinearMap.norm_mkPiAlgebraFin_le + +lemma alternatingGeometricSeries_apply_norm [NormOneClass A] (n : ℕ) : + ‖alternatingGeometricSeries 𝕜 A n‖ = 1 := by + simp [alternatingGeometricSeries] + +lemma one_le_alternatingGeometricSeries_radius [Nontrivial A] : + 1 ≤ (alternatingGeometricSeries 𝕜 A).radius := by + simpa only [FormalMultilinearSeries.radius_compNeg, + alternatingGeometricSeries_eq_formalMultilinearSeries_geometric_comp_neg] + using one_le_formalMultilinearSeries_geometric_radius 𝕜 A + +lemma alternatingGeometricSeries_radius [NormOneClass A] : + (alternatingGeometricSeries 𝕜 A).radius = 1 := + FormalMultilinearSeries.ofScalars_radius_eq_of_tendsto A _ one_ne_zero (by simp) + +lemma hasFPowerSeriesOnBall_inverse_one_add [HasSummableGeomSeries A] [Nontrivial A] : + HasFPowerSeriesOnBall (fun x : A ↦ Ring.inverse (1 + x)) + (alternatingGeometricSeries 𝕜 A) 0 1 := by + rw [alternatingGeometricSeries_eq_formalMultilinearSeries_geometric_comp_neg] + convert_to HasFPowerSeriesOnBall ((fun x ↦ Ring.inverse (1 - x)) ∘ (-ContinuousLinearMap.id 𝕜 A)) + ((formalMultilinearSeries_geometric 𝕜 A).compContinuousLinearMap (-ContinuousLinearMap.id 𝕜 A)) + 0 1 + · ext; simp + convert HasFPowerSeriesOnBall.compContinuousLinearMap _ (r := 1) + · simp [← ofReal_norm] + · simpa using (hasFPowerSeriesOnBall_inverse_one_sub 𝕜 A) + +@[fun_prop] +lemma analyticAt_inverse_one_add [HasSummableGeomSeries A] [Nontrivial A] : + AnalyticAt 𝕜 (fun x : A ↦ Ring.inverse (1 + x)) 0 := + ⟨_, ⟨_, hasFPowerSeriesOnBall_inverse_one_add 𝕜 A⟩⟩ + end Geometric /-- If `A` is a normed algebra over `𝕜` with summable geometric series, then inversion on `A` is @@ -871,6 +983,17 @@ variable (𝕝) in lemma analyticAt_inv_one_sub : AnalyticAt 𝕜 (fun x : 𝕝 ↦ (1 - x)⁻¹) 0 := ⟨_, ⟨_, hasFPowerSeriesOnBall_inv_one_sub 𝕜 𝕝⟩⟩ +variable (𝕜 𝕝) in +lemma hasFPowerSeriesOnBall_inv_one_add : + HasFPowerSeriesOnBall (fun x : 𝕝 ↦ (1 + x)⁻¹) (alternatingGeometricSeries 𝕜 𝕝) 0 1 := by + convert! hasFPowerSeriesOnBall_inverse_one_add 𝕜 𝕝 + exact Ring.inverse_eq_inv'.symm + +variable (𝕝) in +@[fun_prop] +lemma analyticAt_inv_one_add : AnalyticAt 𝕜 (fun x : 𝕝 ↦ (1 + x)⁻¹) 0 := + ⟨_, ⟨_, hasFPowerSeriesOnBall_inv_one_add 𝕜 𝕝⟩⟩ + /-- If `𝕝` is a normed field extension of `𝕜`, then the inverse map `𝕝 → 𝕝` is `𝕜`-analytic away from 0. -/ @[fun_prop] @@ -1159,68 +1282,3 @@ theorem HasFPowerSeriesWithinAt.unshift (hf : HasFPowerSeriesWithinAt f pf s x) hrf.unshift.hasFPowerSeriesWithinAt end - -/-! -### Composition with a linear map --/ - -section compContinuousLinearMap - -variable {u : E →L[𝕜] F} {f : F → G} {pf : FormalMultilinearSeries 𝕜 F G} {s : Set F} {x : E} - {r : ℝ≥0∞} - -theorem HasFPowerSeriesWithinOnBall.compContinuousLinearMap - (hf : HasFPowerSeriesWithinOnBall f pf s (u x) r) : - HasFPowerSeriesWithinOnBall (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x (r / ‖u‖ₑ) where - r_le := by - calc - _ ≤ pf.radius / ‖u‖ₑ := by - gcongr - exact hf.r_le - _ ≤ _ := pf.div_le_radius_compContinuousLinearMap _ - r_pos := by - simp only [ENNReal.div_pos_iff, ne_eq, enorm_ne_top, not_false_eq_true, and_true] - exact pos_iff_ne_zero.mp hf.r_pos - hasSum hy1 hy2 := by - convert! hf.hasSum _ _ - · simp - · simp only [Set.mem_insert_iff, add_eq_left, Set.mem_preimage, map_add] at hy1 ⊢ - rcases hy1 with (hy1 | hy1) <;> simp [hy1] - · simp only [Metric.eball, edist_zero_right, Set.mem_setOf_eq] at hy2 ⊢ - exact lt_of_le_of_lt (ContinuousLinearMap.le_opNorm_enorm _ _) (mul_lt_of_lt_div' hy2) - -theorem HasFPowerSeriesOnBall.compContinuousLinearMap (hf : HasFPowerSeriesOnBall f pf (u x) r) : - HasFPowerSeriesOnBall (f ∘ u) (pf.compContinuousLinearMap u) x (r / ‖u‖ₑ) := by - rw [← hasFPowerSeriesWithinOnBall_univ] at hf ⊢ - exact hf.compContinuousLinearMap - -theorem HasFPowerSeriesAt.compContinuousLinearMap (hf : HasFPowerSeriesAt f pf (u x)) : - HasFPowerSeriesAt (f ∘ u) (pf.compContinuousLinearMap u) x := - let ⟨r, hr⟩ := hf - ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩ - -theorem HasFPowerSeriesWithinAt.compContinuousLinearMap - (hf : HasFPowerSeriesWithinAt f pf s (u x)) : - HasFPowerSeriesWithinAt (f ∘ u) (pf.compContinuousLinearMap u) (u ⁻¹' s) x := - let ⟨r, hr⟩ := hf - ⟨r / ‖u‖ₑ, hr.compContinuousLinearMap⟩ - -theorem AnalyticAt.compContinuousLinearMap (hf : AnalyticAt 𝕜 f (u x)) : - AnalyticAt 𝕜 (f ∘ u) x := - let ⟨p, hp⟩ := hf - ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩ - -theorem AnalyticAtWithin.compContinuousLinearMap (hf : AnalyticWithinAt 𝕜 f s (u x)) : - AnalyticWithinAt 𝕜 (f ∘ u) (u ⁻¹' s) x := - let ⟨p, hp⟩ := hf - ⟨p.compContinuousLinearMap u, hp.compContinuousLinearMap⟩ - -theorem AnalyticOn.compContinuousLinearMap (hf : AnalyticOn 𝕜 f s) : - AnalyticOn 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx => - AnalyticAtWithin.compContinuousLinearMap (hf (u x) hx) - -theorem AnalyticOnNhd.compContinuousLinearMap (hf : AnalyticOnNhd 𝕜 f s) : - AnalyticOnNhd 𝕜 (f ∘ u) (u ⁻¹' s) := fun x hx => - AnalyticAt.compContinuousLinearMap (hf (u x) hx) - -end compContinuousLinearMap diff --git a/Mathlib/Analysis/Analytic/Inverse.lean b/Mathlib/Analysis/Analytic/Inverse.lean index b9b860c5086d23..5a1ae3790ef0d7 100644 --- a/Mathlib/Analysis/Analytic/Inverse.lean +++ b/Mathlib/Analysis/Analytic/Inverse.lean @@ -133,8 +133,7 @@ theorem leftInv_comp (p : FormalMultilinearSeries 𝕜 E F) (i : E ≃L[𝕜] F) (p.leftInv i x (n + 2) fun j : Fin (n + 2) => p 1 fun _ => v j) = -∑ c ∈ {c : Composition (n + 2) | c.length < n + 2}.toFinset, (p.leftInv i x c.length) (p.applyComposition c v) := by - simp only [leftInv, ContinuousMultilinearMap.neg_apply, neg_inj, - ContinuousMultilinearMap.sum_apply] + simp only [leftInv, _root_.neg_apply, neg_inj, _root_.sum_apply] convert! (sum_toFinset_eq_subtype (fun c : Composition (n + 2) => c.length < n + 2) (fun c : Composition (n + 2) => diff --git a/Mathlib/Analysis/Analytic/IsolatedZeros.lean b/Mathlib/Analysis/Analytic/IsolatedZeros.lean index 1fcc5670191e2d..9bad403947a195 100644 --- a/Mathlib/Analysis/Analytic/IsolatedZeros.lean +++ b/Mathlib/Analysis/Analytic/IsolatedZeros.lean @@ -103,7 +103,7 @@ theorem eq_pow_order_mul_iterate_dslope (hp : HasFPowerSeriesAt f p z₀) (z : f z = (z - z₀) ^ p.order • (swap dslope z₀)^[p.order] f z := by refine (pow_sub_smul_iterate_dslope_of_zero _ (fun k hk ↦ ?_) z).symm rw [← (has_fpower_series_iterate_dslope_fslope k hp).coeff_zero 1, ← coeff, coeff_iterate_fslope, - zero_add, coeff, p.apply_eq_zero_of_lt_order hk, ContinuousMultilinearMap.zero_apply] + zero_add, coeff, p.apply_eq_zero_of_lt_order hk, _root_.zero_apply] theorem locally_ne_zero (hp : HasFPowerSeriesAt f p z₀) (h : p ≠ 0) : ∀ᶠ z in 𝓝[≠] z₀, f z ≠ 0 := by rw [eventually_nhdsWithin_iff] diff --git a/Mathlib/Analysis/Analytic/IteratedFDeriv.lean b/Mathlib/Analysis/Analytic/IteratedFDeriv.lean index 2019cd650302ed..2d519981981421 100644 --- a/Mathlib/Analysis/Analytic/IteratedFDeriv.lean +++ b/Mathlib/Analysis/Analytic/IteratedFDeriv.lean @@ -99,8 +99,7 @@ lemma FormalMultilinearSeries.iteratedFDerivSeries_eq_zero {k n : ℕ} ContinuousLinearMap.compFormalMultilinearSeries_apply, ContinuousLinearMap.compContinuousMultilinearMap_coe, ContinuousLinearEquiv.coe_coe, LinearIsometryEquiv.coe_toContinuousLinearEquiv, Function.comp_apply, - continuousMultilinearCurryLeftEquiv_symm_apply, ContinuousMultilinearMap.zero_apply, - _root_.zero_apply, + continuousMultilinearCurryLeftEquiv_symm_apply, _root_.zero_apply, derivSeries_eq_zero _ (ih (p.congr_zero (Nat.succ_add_eq_add_succ _ _).symm h))] /-- If the `n`-th term in a power series is zero, then the `n`-th derivative of the corresponding @@ -111,8 +110,7 @@ lemma HasFPowerSeriesWithinOnBall.iteratedFDerivWithin_eq_zero iteratedFDerivWithin 𝕜 n f s x = 0 := by have : iteratedFDerivWithin 𝕜 n f s x = p.iteratedFDerivSeries n 0 (fun _ ↦ 0) := ((h.iteratedFDerivWithin h' n hu hx).coeff_zero _).symm - rw [this, p.iteratedFDerivSeries_eq_zero (p.congr_zero (Nat.zero_add n).symm hn), - ContinuousMultilinearMap.zero_apply] + rw [this, p.iteratedFDerivSeries_eq_zero (p.congr_zero (Nat.zero_add n).symm hn), zero_apply] lemma ContinuousMultilinearMap.iteratedFDeriv_comp_diagonal {n : ℕ} (f : E [×n]→L[𝕜] F) (x : E) (v : Fin n → E) : @@ -122,7 +120,7 @@ lemma ContinuousMultilinearMap.iteratedFDeriv_comp_diagonal change iteratedFDeriv 𝕜 n (f ∘ g) x v = _ rw [ContinuousLinearMap.iteratedFDeriv_comp_right _ f.contDiff _ le_rfl, f.iteratedFDeriv_eq] simp only [ContinuousMultilinearMap.iteratedFDeriv, - ContinuousMultilinearMap.compContinuousLinearMap_apply, ContinuousMultilinearMap.sum_apply, + ContinuousMultilinearMap.compContinuousLinearMap_apply, sum_apply, ContinuousMultilinearMap.iteratedFDerivComponent_apply, Set.mem_range, Pi.compRightL_apply] rw [← sum_comp (Equiv.embeddingEquivOfFinite (Fin n))] congr with σ diff --git a/Mathlib/Analysis/Analytic/RadiusLiminf.lean b/Mathlib/Analysis/Analytic/RadiusLiminf.lean index caed24f310e47f..ccc2f2d675757f 100644 --- a/Mathlib/Analysis/Analytic/RadiusLiminf.lean +++ b/Mathlib/Analysis/Analytic/RadiusLiminf.lean @@ -10,6 +10,7 @@ public import Mathlib.Analysis.SpecialFunctions.Pow.NNReal /-! # Representation of `FormalMultilinearSeries.radius` as a `liminf` + In this file we prove that the radius of convergence of a `FormalMultilinearSeries` is equal to $\liminf_{n\to\infty} \frac{1}{\sqrt[n]{‖p n‖}}$. This lemma can't go to `Analysis.Analytic.Basic` because this would create a circular dependency once we redefine `exp` using diff --git a/Mathlib/Analysis/Asymptotics/Lemmas.lean b/Mathlib/Analysis/Asymptotics/Lemmas.lean index fed7b0cf783739..026a39b13ca486 100644 --- a/Mathlib/Analysis/Asymptotics/Lemmas.lean +++ b/Mathlib/Analysis/Asymptotics/Lemmas.lean @@ -192,11 +192,21 @@ theorem IsLittleO.trans_tendsto (hfg : f'' =o[l] g'') (hg : Tendsto g'' l (𝓝 lemma isLittleO_id_one [One F''] [NeZero (1 : F'')] : (fun x : E'' => x) =o[𝓝 0] (1 : E'' → F'') := isLittleO_id_const one_ne_zero -theorem continuousAt_iff_isLittleO {α : Type*} {E : Type*} [NormedRing E] [NormOneClass E] +theorem continuousAt_iff_isLittleO {α : Type*} {E : Type*} [NormedRing E] [One F] [NormOneClass F] [TopologicalSpace α] {f : α → E} {x : α} : - (ContinuousAt f x) ↔ (fun (y : α) ↦ f y - f x) =o[𝓝 x] (fun (_ : α) ↦ (1 : E)) := by + (ContinuousAt f x) ↔ (f · - f x) =o[𝓝 x] (fun (_ : α) ↦ (1 : F)) := by simp [ContinuousAt, ← tendsto_sub_nhds_zero_iff] +theorem _root_.ContinuousAt.isLittleO {α : Type*} {E : Type*} [NormedRing E] [One F] + [NormOneClass F] [TopologicalSpace α] {f : α → E} {x : α} (hcont : ContinuousAt f x) : + (f · - f x) =o[𝓝 x] (fun _ ↦ (1 : F)) := + continuousAt_iff_isLittleO.mp hcont + +theorem _root_.ContinuousAt.isBigO {α : Type*} {E : Type*} [NormedRing E] [One F] [NormOneClass F] + [TopologicalSpace α] {f : α → E} {x : α} (hcont : ContinuousAt f x) : + f =O[𝓝 x] (fun _ ↦ (1 : F)) := + hcont.isLittleO.isBigO.congr_of_sub.mpr (isBigO_const_one ..) + /-! ### Multiplication -/ theorem IsBigO.of_pow {f : α → 𝕜} {g : α → R} {n : ℕ} (hn : n ≠ 0) (h : (f ^ n) =O[l] (g ^ n)) : diff --git a/Mathlib/Analysis/Asymptotics/SpecificAsymptotics.lean b/Mathlib/Analysis/Asymptotics/SpecificAsymptotics.lean index d3f80d779673f2..5b01e03ea44e57 100644 --- a/Mathlib/Analysis/Asymptotics/SpecificAsymptotics.lean +++ b/Mathlib/Analysis/Asymptotics/SpecificAsymptotics.lean @@ -18,10 +18,7 @@ theory developed in `Mathlib/Analysis/Asymptotics/Defs.lean` and public section - -open Filter Asymptotics - -open Topology +open Bornology Filter Asymptotics Set Topology section NormedField @@ -209,3 +206,66 @@ theorem Asymptotics.isEquivalent_nat_ceil : isEquivalent_of_tendsto_one tendsto_nat_ceil_div_atTop end NormedLinearOrderedField + +section boundedRange + +/-! +## Bounded Range versus `IsBigO` Asymptotics + +For a continuous function `f` into a seminormed space, defined on an unbounded linear order whose +order topology has compact intervals, having bounded range is equivalent to being `O(1)` along both +`atTop` and `atBot` (`Continuous.isBounded_range_iff_isBigO_atTop_atBot`). For an even function a +single `O(1)` bound along `atTop` already suffices +(`Continuous.isBounded_range_iff_isBigO_atTop_of_even`), since `Function.Even` transports an `atTop` +bound to an `atBot` bound (`Function.Even.isBigO_atTop_iff_isBigO_atBot`). +-/ + +variable + {E : Type*} [SeminormedAddCommGroup E] + {D : Type*} [TopologicalSpace D] + {β : Type*} [TopologicalSpace β] [LinearOrder β] [OrderClosedTopology β] [CompactIccSpace β] + [NoMaxOrder β] [NoMinOrder β] + +/-- +A continuous function `f` has bounded range if and only if it is `O(1)` with respect to the +cocompact filter. +-/ +theorem Continuous.isBounded_range_iff_isBigO {f : D → E} (hf : Continuous f) : + IsBounded (range f) ↔ f =O[cocompact D] (1 : D → ℝ) := by + constructor <;> intro h + · rw [isBounded_iff_forall_norm_le] at h + obtain ⟨c, hc⟩ := h + simp only [Set.mem_range, forall_exists_index, forall_apply_eq_imp_iff] at hc + rw [isBigO_iff] + use c + apply Eventually.of_forall + simpa using hc + · simp_rw [isBigO_iff, Filter.Eventually, Filter.mem_cocompact] at h + simp only [Pi.one_apply, norm_one, mul_one] at h + obtain ⟨c, t, hcompact, h⟩ := h + rw [← Set.image_union_image_compl_eq_range (s := t)] + apply IsBounded.union + · apply (IsCompact.image hcompact hf).isBounded + · rw [isBounded_iff_forall_norm_le] + refine ⟨c, fun x hx ↦ ?_⟩ + rw [Set.mem_image] at hx + obtain ⟨y, hy, rfl⟩ := hx + simpa using mem_of_mem_of_subset hy h + +/-- +A continuous function `f` on an unbounded linear order with compact intervals has bounded range if +and only if it is `O(1)` at both `atTop` and `atBot`. +-/ +theorem Continuous.isBounded_range_iff_isBigO_atTop_atBot {f : β → E} (hf : Continuous f) : + IsBounded (range f) ↔ f =O[atTop] (1 : β → ℝ) ∧ f =O[atBot] (1 : β → ℝ) := by + rw [hf.isBounded_range_iff_isBigO, cocompact_eq_atBot_atTop, isBigO_sup, and_comm] + +/-- A continuous even function has bounded range if and only if `f =O[atTop] 1`. -/ +theorem Continuous.isBounded_range_iff_isBigO_atTop_of_even [AddCommGroup β] [IsOrderedAddMonoid β] + {f : β → E} (hf : Continuous f) (heven : Function.Even f) : + IsBounded (range f) ↔ f =O[atTop] (1 : β → ℝ) := + ⟨fun h ↦ (hf.isBounded_range_iff_isBigO_atTop_atBot.mp h).1, + fun h ↦ hf.isBounded_range_iff_isBigO_atTop_atBot.mpr + ⟨h, by simpa only [← neg_atTop, ← Filter.map_neg, isBigO_map, Function.comp_def, heven.eq]⟩⟩ + +end boundedRange diff --git a/Mathlib/Analysis/CStarAlgebra/Basic.lean b/Mathlib/Analysis/CStarAlgebra/Basic.lean index d8a77466ce59d5..59c88f4ea228c1 100644 --- a/Mathlib/Analysis/CStarAlgebra/Basic.lean +++ b/Mathlib/Analysis/CStarAlgebra/Basic.lean @@ -90,8 +90,7 @@ class CStarRing (E : Type*) [NonUnitalNormedRing E] [StarRing E] : Prop where norm_mul_self_le : ∀ x : E, ‖x‖ * ‖x‖ ≤ ‖x⋆ * x‖ instance : CStarRing ℝ where - norm_mul_self_le x := by - simp only [Real.norm_eq_abs, abs_mul_abs_self, star, id, norm_mul, le_refl] + norm_mul_self_le x := by simp namespace CStarRing diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean index be142dd60e974d..303415eb28ff4d 100644 --- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean +++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/NonUnital.lean @@ -580,7 +580,7 @@ lemma cfcₙ_neg : cfcₙ (fun x ↦ -(f x)) a = -(cfcₙ f a) := by obtain (ha | hf | h0) := h · simp [cfcₙ_apply_of_not_predicate a ha] · rw [cfcₙ_apply_of_not_continuousOn a hf, cfcₙ_apply_of_not_continuousOn, neg_zero] - exact fun hf_neg ↦ hf <| by simpa using hf_neg.neg + exact fun hf_neg ↦ hf <| by simpa using hf_neg.fun_neg · rw [cfcₙ_apply_of_not_map_zero a h0, cfcₙ_apply_of_not_map_zero, neg_zero] exact (h0 <| neg_eq_zero.mp ·) diff --git a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean index b72f01d4f986e4..e1daf6ea7a326d 100644 --- a/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean +++ b/Mathlib/Analysis/CStarAlgebra/ContinuousFunctionalCalculus/Unital.lean @@ -770,7 +770,7 @@ noncomputable def cfcUnits (hf' : ∀ x ∈ spectrum R a, f x ≠ 0) lemma cfcUnits_pow (hf' : ∀ x ∈ spectrum R a, f x ≠ 0) (n : ℕ) (hf : ContinuousOn f (spectrum R a) := by cfc_cont_tac) (ha : p a := by cfc_tac) : (cfcUnits f a hf') ^ n = - cfcUnits _ _ (forall₂_imp (fun _ _ ↦ pow_ne_zero n) hf') (hf := hf.pow n) := by + cfcUnits _ _ (forall₂_imp (fun _ _ ↦ pow_ne_zero n) hf') (hf := hf.fun_pow n) := by ext cases n with | zero => simp [cfc_const_one R a] @@ -882,7 +882,7 @@ lemma cfc_neg : cfc (fun x ↦ -(f x)) a = -(cfc f a) := by · obtain (ha | hf) := not_and_or.mp h · simp [cfc_apply_of_not_predicate a ha] · rw [cfc_apply_of_not_continuousOn a hf, cfc_apply_of_not_continuousOn, neg_zero] - exact fun hf_neg ↦ hf <| by simpa using hf_neg.neg + exact fun hf_neg ↦ hf <| by simpa using hf_neg.fun_neg lemma cfc_neg' : cfc (-f) = (-cfc f : A → A) := by ext1 a; exact cfc_neg f a diff --git a/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean b/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean index 8fa4b863c83f0b..68ae5a326fbd8f 100644 --- a/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean +++ b/Mathlib/Analysis/CStarAlgebra/GelfandDuality.lean @@ -91,7 +91,7 @@ noncomputable def Ideal.toCharacterSpace : characterSpace ℂ A := theorem Ideal.toCharacterSpace_apply_eq_zero_of_mem {a : A} (ha : a ∈ I) : I.toCharacterSpace a = 0 := by unfold Ideal.toCharacterSpace - simp only [CharacterSpace.equivAlgHom_symm_coe, AlgHom.coe_comp, AlgEquiv.coe_algHom, + simp only [CharacterSpace.equivAlgHom_symm_coe, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, Quotient.mkₐ_eq_mk, Function.comp_apply, NormedRing.algEquivComplexOfComplete_symm_apply] simp_rw [Quotient.eq_zero_iff_mem.mpr ha, spectrum.zero_eq] exact Set.eq_of_mem_singleton (Set.singleton_nonempty (0 : ℂ)).some_mem diff --git a/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean b/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean index 812a16c805f531..3a577b00729dab 100644 --- a/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean +++ b/Mathlib/Analysis/CStarAlgebra/GelfandNaimarkSegal.lean @@ -135,10 +135,12 @@ consequence. -/ @[simp] private lemma completion_leftMulMapPreGNS_map_smul (m : ℂ) (x : A) : - (f.leftMulMapPreGNS (m • x)).completion = m • (f.leftMulMapPreGNS x).completion := by + (f.leftMulMapPreGNS (m • x)).completion = m • (f.leftMulMapPreGNS x).completion := by ext a induction a using induction_on with - | hp => apply isClosed_eq <;> fun_prop + | hp => + exact isClosed_eq (f.leftMulMapPreGNS (m • x)).completion.continuous + (m • (f.leftMulMapPreGNS x).completion).continuous | ih a => simp [smul_mul_assoc] /-- diff --git a/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean b/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean index a5d0adb3aab838..22ba7dd887b9cf 100644 --- a/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean +++ b/Mathlib/Analysis/CStarAlgebra/PositiveLinearMap.lean @@ -31,23 +31,6 @@ open scoped NNReal variable {A₁ A₂ B₁ B₂ : Type*} -section CFC - -variable [NonUnitalRing A₁] [Module ℂ A₁] [SMulCommClass ℝ A₁ A₁] [IsScalarTower ℝ A₁ A₁] - [StarRing A₁] [TopologicalSpace A₁] [NonUnitalContinuousFunctionalCalculus ℝ A₁ IsSelfAdjoint] - [PartialOrder A₁] [StarOrderedRing A₁] - -variable [NonUnitalRing A₂] [Module ℂ A₂] [StarRing A₂] [PartialOrder A₂] [StarOrderedRing A₂] - -@[aesop safe apply (rule_sets := [CStarAlgebra])] -lemma map_isSelfAdjoint (f : A₁ →ₚ[ℂ] A₂) (a : A₁) (ha : IsSelfAdjoint a) : - IsSelfAdjoint (f a) := by - rw [← CFC.posPart_sub_negPart a ha] - cfc_tac - -end CFC - - section CStarAlgebra namespace PositiveLinearMap @@ -134,16 +117,6 @@ instance {F : Type*} [FunLike F A₁ A₂] [LinearMapClass F ℂ A₁ A₂] [Ord exact ⟨C, h⟩ exact (LinearMap.mkContinuousOfExistsBound (f : A₁ →ₗ[ℂ] A₂) hbound).continuous -instance {F : Type*} [FunLike F A₁ A₂] [LinearMapClass F ℂ A₁ A₂] [OrderHomClass F A₁ A₂] : - StarHomClass F A₁ A₂ where - map_star f a := by - obtain ⟨y, hy_nonneg, hy_norm, hy⟩ := CStarAlgebra.exists_sum_four_nonneg a - have hy' : ∀ x : Fin 4, star (y x) = y x := fun x => by - rw [IsSelfAdjoint.star_eq (hy_nonneg x).isSelfAdjoint] - have hy'' : ∀ x : Fin 4, star (f (y x)) = f (y x) := fun x => by - rw [IsSelfAdjoint.star_eq (map_nonneg f (hy_nonneg x)).isSelfAdjoint] - simp [hy, hy', hy''] - end PositiveLinearMap end CStarAlgebra diff --git a/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean b/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean index ab6c20e1b8b863..ac8d947d77b276 100644 --- a/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean +++ b/Mathlib/Analysis/CStarAlgebra/Unitary/Connected.lean @@ -47,7 +47,8 @@ products of exponential unitaries. `expUnitary x` for a selfadjoint element `x`. + `Unitary.isPathConnected_ball`: any ball of radius `δ < 2` in the unitary group of a unital C⋆-algebra is path connected. -+ `Unitary.instLocPathConnectedSpace`: the unitary group of a C⋆-algebra is locally path connected. ++ `Unitary.instLocallyPathConnectedSpace`: the unitary group of a C⋆-algebra is + locally path connected. + `Unitary.mem_pathComponentOne_iff`: The path component of the identity in the unitary group of a C⋆-algebra is the set of unitaries that can be expressed as a product of exponentials of selfadjoint elements. @@ -326,7 +327,7 @@ lemma Unitary.isPathConnected_ball (u : unitary A) (δ : ℝ) (hδ₀ : 0 < δ) norm_expUnitary_smul_argSelfAdjoint_sub_one_le u t.2 (hu.trans hδ₂) |>.trans_lt hu /-- The unitary group in a C⋆-algebra is locally path connected. -/ -instance Unitary.instLocPathConnectedSpace : LocPathConnectedSpace (unitary A) := +instance Unitary.instLocallyPathConnectedSpace : LocallyPathConnectedSpace (unitary A) := .of_bases (fun _ ↦ nhds_basis_uniformity <| uniformity_basis_dist_lt zero_lt_two) <| by simpa using! isPathConnected_ball diff --git a/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean b/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean index 79825130a34cf8..2bf4bd218d8658 100644 --- a/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean +++ b/Mathlib/Analysis/Calculus/AbsolutelyMonotone.lean @@ -103,8 +103,7 @@ theorem add (hf : AbsolutelyMonotoneOn f s) (hg : AbsolutelyMonotoneOn g s) : obtain ⟨p, hp, hp_nn⟩ := hf obtain ⟨q, hq, hq_nn⟩ := hg refine ⟨p + q, hp.add hq, fun n x hx => ?_⟩ - simp only [Pi.add_apply, FormalMultilinearSeries.add_apply, - ContinuousMultilinearMap.add_apply] + simp only [Pi.add_apply, FormalMultilinearSeries.add_apply, add_apply] exact add_nonneg (hp_nn n hx) (hq_nn n hx) /-- A nonnegative scalar multiple of an absolutely monotone function is absolutely monotone. -/ diff --git a/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean b/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean index 53ded4d8e91d6e..084664a10b855d 100644 --- a/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean +++ b/Mathlib/Analysis/Calculus/ContDiff/FTaylorSeries.lean @@ -7,6 +7,7 @@ module public import Mathlib.Analysis.Calculus.FDeriv.Add public import Mathlib.Analysis.Calculus.FDeriv.Equiv +public import Mathlib.Analysis.Calculus.FDeriv.CompCLM public import Mathlib.Analysis.Calculus.FormalMultilinearSeries public import Mathlib.Data.ENat.Lattice @@ -829,6 +830,14 @@ theorem iteratedFDeriv_succ_apply_left {n : ℕ} (m : Fin (n + 1) → E) : (fderiv 𝕜 (iteratedFDeriv 𝕜 n f) x : E → E [×n]→L[𝕜] F) (m 0) (tail m) := rfl +/-- The iterated derivative is given by the derivative of the `n-1` iterated derivative. -/ +theorem DifferentiableAt.iteratedFDeriv_succ_apply_left' {n : ℕ} {m : Fin (n + 1) → E} + (hf : DifferentiableAt 𝕜 (iteratedFDeriv 𝕜 n f) x) : + iteratedFDeriv 𝕜 (n + 1) f x m = + fderiv 𝕜 (fun y ↦ iteratedFDeriv 𝕜 n f y (Fin.tail m)) x (m 0) := by + convert iteratedFDeriv_succ_apply_left m + simp [fderiv_continuousMultilinear_apply_const hf] + /-- Writing explicitly the `n+1`-th derivative as the composition of a currying linear equiv, and the derivative of the `n`-th derivative. -/ theorem iteratedFDeriv_succ_eq_comp_left {n : ℕ} : diff --git a/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean b/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean index 6b24f9ee7aaf84..5ae2154bdc47b5 100644 --- a/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean +++ b/Mathlib/Analysis/Calculus/ContDiff/FaaDiBruno.lean @@ -1100,7 +1100,6 @@ theorem HasFTaylorSeriesUpToOn.comp {n : WithTop ℕ∞} {g : F → G} {f : E convert! B ext v simp only [Nat.succ_eq_add_one, Fintype.sum_option, ContinuousMultilinearMap.curryLeft_apply, - ContinuousMultilinearMap.sum_apply, ContinuousMultilinearMap.add_apply, FormalMultilinearSeries.compAlongOrderedFinpartition_apply, sum_apply, add_apply] rw [Finset.sum_sigma'] exact Fintype.sum_equiv (OrderedFinpartition.extendEquiv m) _ _ (fun p ↦ rfl) diff --git a/Mathlib/Analysis/Calculus/ContDiff/Operations.lean b/Mathlib/Analysis/Calculus/ContDiff/Operations.lean index 7fb93ae9a9881d..fcd9c1301e8602 100644 --- a/Mathlib/Analysis/Calculus/ContDiff/Operations.lean +++ b/Mathlib/Analysis/Calculus/ContDiff/Operations.lean @@ -284,8 +284,7 @@ theorem iteratedFDerivWithin_neg_apply {f : E → F} (hu : UniqueDiffOn 𝕜 s) _ = fderivWithin 𝕜 (-iteratedFDerivWithin 𝕜 i f s) s x (h 0) (Fin.tail h) := by rw [fderivWithin_congr' (@hi) hx, Pi.neg_def] _ = -(fderivWithin 𝕜 (iteratedFDerivWithin 𝕜 i f s) s) x (h 0) (Fin.tail h) := by - rw [fderivWithin_neg (hu x hx), neg_apply, - ContinuousMultilinearMap.neg_apply] + rw [fderivWithin_neg (hu x hx), neg_apply, neg_apply] _ = -(iteratedFDerivWithin 𝕜 (i + 1) f s) x h := by rw [iteratedFDerivWithin_succ_apply_left] @@ -690,8 +689,7 @@ theorem iteratedFDeriv_comp_const_smul (a : 𝕜) (hf : ContDiff 𝕜 i f) : ext v rw [iteratedFDeriv_succ_eq_comp_left, iteratedFDeriv_succ_eq_comp_left] simp only [Nat.succ_eq_add_one, Nat.cast_add, Nat.cast_one, self_le_add_right, hf.of_le, hi, - comp_apply, continuousMultilinearCurryLeftEquiv_symm_apply, - ContinuousMultilinearMap.smul_apply] + comp_apply, continuousMultilinearCurryLeftEquiv_symm_apply, smul_apply] rw [fderiv_fun_const_smul, fderiv_comp_smul, smul_smul, ← pow_succ] · simp rw [← Function.comp_def (g := (a • ·))] @@ -795,50 +793,46 @@ theorem contDiffOn_inv {n} : ContDiffOn 𝕜 n (Inv.inv : 𝕜' → 𝕜') {0} variable {𝕜} -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem ContDiffWithinAt.inv {f : E → 𝕜'} {n} (hf : ContDiffWithinAt 𝕜 n f s x) (hx : f x ≠ 0) : - ContDiffWithinAt 𝕜 n (fun x => (f x)⁻¹) s x := + ContDiffWithinAt 𝕜 n f⁻¹ s x := (contDiffAt_inv 𝕜 hx).comp_contDiffWithinAt x hf -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem ContDiffOn.inv {f : E → 𝕜'} (hf : ContDiffOn 𝕜 n f s) (h : ∀ x ∈ s, f x ≠ 0) : - ContDiffOn 𝕜 n (fun x => (f x)⁻¹) s := fun x hx => (hf.contDiffWithinAt hx).inv (h x hx) + ContDiffOn 𝕜 n f⁻¹ s := fun x hx => (hf.contDiffWithinAt hx).inv (h x hx) -@[fun_prop] +@[to_fun (attr := fun_prop)] nonrec theorem ContDiffAt.inv {f : E → 𝕜'} (hf : ContDiffAt 𝕜 n f x) (hx : f x ≠ 0) : - ContDiffAt 𝕜 n (fun x => (f x)⁻¹) x := + ContDiffAt 𝕜 n f⁻¹ x := hf.inv hx -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem ContDiff.inv {f : E → 𝕜'} (hf : ContDiff 𝕜 n f) (h : ∀ x, f x ≠ 0) : - ContDiff 𝕜 n fun x => (f x)⁻¹ := by + ContDiff 𝕜 n f⁻¹ := by rw [contDiff_iff_contDiffAt]; exact fun x => hf.contDiffAt.inv (h x) -- TODO: generalize to `f g : E → 𝕜'` -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem ContDiffWithinAt.div {f g : E → 𝕜} {n} (hf : ContDiffWithinAt 𝕜 n f s x) (hg : ContDiffWithinAt 𝕜 n g s x) (hx : g x ≠ 0) : - ContDiffWithinAt 𝕜 n (fun x => f x / g x) s x := by - simpa only [div_eq_mul_inv] using hf.mul (hg.inv hx) + ContDiffWithinAt 𝕜 n (f / g) s x := by + change ContDiffWithinAt 𝕜 n (fun x => f x / g x) s x + simpa only [div_eq_mul_inv] using hf.mul (hg.fun_inv hx) -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem ContDiffOn.div {f g : E → 𝕜} {n} (hf : ContDiffOn 𝕜 n f s) (hg : ContDiffOn 𝕜 n g s) (h₀ : ∀ x ∈ s, g x ≠ 0) : ContDiffOn 𝕜 n (f / g) s := fun x hx => (hf x hx).div (hg x hx) (h₀ x hx) -@[fun_prop] -theorem ContDiffOn.fun_div {f g : E → 𝕜} {n} (hf : ContDiffOn 𝕜 n f s) - (hg : ContDiffOn 𝕜 n g s) (h₀ : ∀ x ∈ s, g x ≠ 0) : ContDiffOn 𝕜 n (fun x => f x / g x) s := - ContDiffOn.div hf hg h₀ - -@[fun_prop] +@[to_fun (attr := fun_prop)] nonrec theorem ContDiffAt.div {f g : E → 𝕜} {n} (hf : ContDiffAt 𝕜 n f x) - (hg : ContDiffAt 𝕜 n g x) (hx : g x ≠ 0) : ContDiffAt 𝕜 n (fun x => f x / g x) x := + (hg : ContDiffAt 𝕜 n g x) (hx : g x ≠ 0) : ContDiffAt 𝕜 n (f / g) x := hf.div hg hx -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem ContDiff.div {f g : E → 𝕜} {n} (hf : ContDiff 𝕜 n f) (hg : ContDiff 𝕜 n g) - (h0 : ∀ x, g x ≠ 0) : ContDiff 𝕜 n fun x => f x / g x := by + (h0 : ∀ x, g x ≠ 0) : ContDiff 𝕜 n (f / g) := by simp only [contDiff_iff_contDiffAt] at * exact fun x => (hf x).div (hg x) (h0 x) diff --git a/Mathlib/Analysis/Calculus/Deriv/Inv.lean b/Mathlib/Analysis/Calculus/Deriv/Inv.lean index 5222e9c6990361..e81a21975b47f2 100644 --- a/Mathlib/Analysis/Calculus/Deriv/Inv.lean +++ b/Mathlib/Analysis/Calculus/Deriv/Inv.lean @@ -98,7 +98,8 @@ theorem fderivWithin_inv (x_ne_zero : x ≠ 0) (hxs : UniqueDiffWithinAt 𝕜 s rw [DifferentiableAt.fderivWithin (differentiableAt_inv x_ne_zero) hxs] exact fderiv_inv -variable {c : 𝕜 → 𝕜} {c' : 𝕜} +variable {𝕜' : Type*} [NontriviallyNormedField 𝕜'] [NormedAlgebra 𝕜 𝕜'] +variable {c : 𝕜 → 𝕜'} {c' : 𝕜'} @[to_fun] theorem HasDerivWithinAt.inv (hc : HasDerivWithinAt c c' s x) (hx : c x ≠ 0) : @@ -183,32 +184,17 @@ theorem DifferentiableWithinAt.div (hc : DifferentiableWithinAt 𝕜 c s x) DifferentiableWithinAt 𝕜 (c / d) s x := hc.fun_div hd hx -@[simp, fun_prop] -theorem DifferentiableAt.fun_div (hc : DifferentiableAt 𝕜 c x) (hd : DifferentiableAt 𝕜 d x) - (hx : d x ≠ 0) : DifferentiableAt 𝕜 (fun x => c x / d x) x := - (hc.hasDerivAt.div hd.hasDerivAt hx).differentiableAt - -@[simp, fun_prop] +@[to_fun (attr := simp, fun_prop)] theorem DifferentiableAt.div (hc : DifferentiableAt 𝕜 c x) (hd : DifferentiableAt 𝕜 d x) (hx : d x ≠ 0) : DifferentiableAt 𝕜 (c / d) x := - hc.fun_div hd hx - -@[fun_prop] -theorem DifferentiableOn.fun_div (hc : DifferentiableOn 𝕜 c s) (hd : DifferentiableOn 𝕜 d s) - (hx : ∀ x ∈ s, d x ≠ 0) : DifferentiableOn 𝕜 (fun x => c x / d x) s := fun x h => - (hc x h).div (hd x h) (hx x h) + (hc.hasDerivAt.div hd.hasDerivAt hx).differentiableAt -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem DifferentiableOn.div (hc : DifferentiableOn 𝕜 c s) (hd : DifferentiableOn 𝕜 d s) (hx : ∀ x ∈ s, d x ≠ 0) : DifferentiableOn 𝕜 (c / d) s := fun x h => (hc x h).div (hd x h) (hx x h) -@[simp, fun_prop] -theorem Differentiable.fun_div (hc : Differentiable 𝕜 c) (hd : Differentiable 𝕜 d) - (hx : ∀ x, d x ≠ 0) : - Differentiable 𝕜 (fun x => c x / d x) := fun x => (hc x).div (hd x) (hx x) - -@[simp, fun_prop] +@[to_fun (attr := simp, fun_prop)] theorem Differentiable.div (hc : Differentiable 𝕜 c) (hd : Differentiable 𝕜 d) (hx : ∀ x, d x ≠ 0) : Differentiable 𝕜 (c / d) := fun x => (hc x).div (hd x) (hx x) diff --git a/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean b/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean index 35fec7e28a8c2f..bb2d7794ab3b86 100644 --- a/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean +++ b/Mathlib/Analysis/Calculus/Deriv/MeanValue.lean @@ -338,7 +338,7 @@ theorem Convex.image_sub_lt_mul_sub_of_deriv_lt {D : Set ℝ} (hD : Convex ℝ D have hf'_gt : ∀ x ∈ interior D, -C < deriv (fun y => -f y) x := fun x hx => by rw [deriv.fun_neg, neg_lt_neg_iff] exact lt_hf' x hx - linarith [hD.mul_sub_lt_image_sub_of_lt_deriv hf.neg hf'.neg hf'_gt x hx y hy hxy] + linarith [hD.mul_sub_lt_image_sub_of_lt_deriv hf.fun_neg hf'.neg hf'_gt x hx y hy hxy] /-- Let `f : ℝ → ℝ` be a differentiable function. If `f' < C`, then `f` grows slower than `C * x` on `D`, i.e., `f y - f x < C * (y - x)` whenever `x < y`. -/ @@ -358,7 +358,7 @@ theorem Convex.image_sub_le_mul_sub_of_deriv_le {D : Set ℝ} (hD : Convex ℝ D have hf'_ge : ∀ x ∈ interior D, -C ≤ deriv (fun y => -f y) x := fun x hx => by rw [deriv.fun_neg, neg_le_neg_iff] exact le_hf' x hx - linarith [hD.mul_sub_le_image_sub_of_le_deriv hf.neg hf'.neg hf'_ge x hx y hy hxy] + linarith [hD.mul_sub_le_image_sub_of_le_deriv hf.fun_neg hf'.neg hf'_ge x hx y hy hxy] /-- Let `f : ℝ → ℝ` be a differentiable function. If `f' ≤ C`, then `f` grows at most as fast as `C * x`, i.e., `f y - f x ≤ C * (y - x)` whenever `x ≤ y`. -/ diff --git a/Mathlib/Analysis/Calculus/Deriv/ZPow.lean b/Mathlib/Analysis/Calculus/Deriv/ZPow.lean index e474eb729abbb5..e52733ae9f6973 100644 --- a/Mathlib/Analysis/Calculus/Deriv/ZPow.lean +++ b/Mathlib/Analysis/Calculus/Deriv/ZPow.lean @@ -168,17 +168,21 @@ theorem iter_deriv_inv_linear_sub (k : ℕ) (c d : 𝕜) : variable {f : E → 𝕜} {t : Set E} {a : E} +@[fun_prop] theorem DifferentiableWithinAt.zpow (hf : DifferentiableWithinAt 𝕜 f t a) (h : f a ≠ 0 ∨ 0 ≤ m) : DifferentiableWithinAt 𝕜 (fun x => f x ^ m) t a := (differentiableAt_zpow.2 h).comp_differentiableWithinAt a hf +@[fun_prop] theorem DifferentiableAt.zpow (hf : DifferentiableAt 𝕜 f a) (h : f a ≠ 0 ∨ 0 ≤ m) : DifferentiableAt 𝕜 (fun x => f x ^ m) a := (differentiableAt_zpow.2 h).comp a hf +@[fun_prop] theorem DifferentiableOn.zpow (hf : DifferentiableOn 𝕜 f t) (h : (∀ x ∈ t, f x ≠ 0) ∨ 0 ≤ m) : DifferentiableOn 𝕜 (fun x => f x ^ m) t := fun x hx => (hf x hx).zpow <| h.imp_left fun h => h x hx +@[fun_prop] theorem Differentiable.zpow (hf : Differentiable 𝕜 f) (h : (∀ x, f x ≠ 0) ∨ 0 ≤ m) : Differentiable 𝕜 fun x => f x ^ m := fun x => (hf x).zpow <| h.imp_left fun h => h x diff --git a/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean b/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean index 117d0433267872..fef5366e1c2606 100644 --- a/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean +++ b/Mathlib/Analysis/Calculus/FDeriv/Analytic.lean @@ -88,7 +88,7 @@ theorem HasFPowerSeriesWithinAt.hasStrictFDerivWithinAt (h : HasFPowerSeriesWith refine h.isBigO_image_sub_norm_mul_norm_sub.trans_isLittleO (IsLittleO.of_norm_right ?_) refine isLittleO_iff_exists_eq_mul.2 ⟨fun y => ‖y - (x, x)‖, ?_, EventuallyEq.rfl⟩ apply Tendsto.mono_left _ nhdsWithin_le_nhds - refine (continuous_id.sub continuous_const).norm.tendsto' _ _ ?_ + refine (continuous_id.fun_sub continuous_const).norm.tendsto' _ _ ?_ rw [_root_.id, sub_self, norm_zero] theorem HasFPowerSeriesAt.hasStrictFDerivAt (h : HasFPowerSeriesAt f p x) : @@ -334,7 +334,7 @@ theorem HasFPowerSeriesWithinOnBall.hasSum_derivSeries_of_hasFDerivWithinAt ContinuousLinearMap.compFormalMultilinearSeries_apply, FormalMultilinearSeries.changeOriginSeries, ContinuousLinearMap.compContinuousMultilinearMap_coe, ContinuousLinearEquiv.coe_coe, - LinearIsometryEquiv.coe_coe, Function.comp_apply, ContinuousMultilinearMap.sum_apply, map_sum] + LinearIsometryEquiv.coe_coe, Function.comp_apply, sum_apply, map_sum] rfl /-- If a function has a power series within a set on a ball, then so does its derivative. Version @@ -591,12 +591,13 @@ theorem changeOrigin_toFormalMultilinearSeries [DecidableEq ι] : cases isEmpty_or_nonempty ι · have (l : _) : 1 + l ≠ Fintype.card ι := by rw [add_comm, Fintype.card_eq_zero]; exact Nat.succ_ne_zero _ - simp_rw [Fintype.sum_empty, changeOriginSeries_support _ (this _), zero_apply _, tsum_zero]; rfl + simp_rw [Fintype.sum_empty, changeOriginSeries_support _ (this _), _root_.zero_apply _, + tsum_zero]; rfl rw [tsum_eq_single (Fintype.card ι - 1), changeOriginSeries]; swap · intro m hm rw [Ne, eq_tsub_iff_add_eq_of_le (by exact Fintype.card_pos), add_comm] at hm - rw [f.changeOriginSeries_support hm, zero_apply] - rw [sum_apply, ContinuousMultilinearMap.sum_apply, Fin.snoc_zero] + rw [f.changeOriginSeries_support hm, _root_.zero_apply] + rw [_root_.sum_apply, _root_.sum_apply, Fin.snoc_zero] simp_rw [changeOriginSeriesTerm_apply] refine (Fintype.sum_bijective (?_ ∘ Fintype.equivFinOfCardEq (Nat.add_sub_of_le Fintype.card_pos).symm) (.comp ?_ <| Equiv.bijective _) _ _ fun i ↦ ?_).symm @@ -795,8 +796,8 @@ theorem derivSeries_apply_diag (n : ℕ) (x : E) : derivSeries p n (fun _ ↦ x) x = (n + 1) • p (n + 1) fun _ ↦ x := by simp only [derivSeries, compFormalMultilinearSeries_apply, changeOriginSeries, compContinuousMultilinearMap_coe, ContinuousLinearEquiv.coe_coe, LinearIsometryEquiv.coe_coe, - Function.comp_apply, ContinuousMultilinearMap.sum_apply, map_sum, _root_.sum_apply, - continuousMultilinearCurryFin1_apply, Matrix.zero_empty] + Function.comp_apply, map_sum, _root_.sum_apply, continuousMultilinearCurryFin1_apply, + Matrix.zero_empty] convert! Finset.sum_const _ · rw [Fin.snoc_zero, changeOriginSeriesTerm_apply, Finset.piecewise_same, add_comm] · rw [← card, card_subtype, ← Finset.powerset_univ, ← Finset.powersetCard_eq_filter, diff --git a/Mathlib/Analysis/Calculus/Gradient/Basic.lean b/Mathlib/Analysis/Calculus/Gradient/Basic.lean index 3722367d7b138f..e954b2d8320958 100644 --- a/Mathlib/Analysis/Calculus/Gradient/Basic.lean +++ b/Mathlib/Analysis/Calculus/Gradient/Basic.lean @@ -118,6 +118,24 @@ alias ⟨HasFDerivAt.hasGradientAt, _⟩ := hasFDerivAt_iff_hasGradientAt theorem gradient_eq_zero_of_not_differentiableAt (h : ¬DifferentiableAt 𝕜 f x) : ∇ f x = 0 := by rw [gradient, fderiv_zero_of_not_differentiableAt h, map_zero] +@[simp] +lemma toDual_gradientWithin : + (toDual 𝕜 F) (gradientWithin f s x) = fderivWithin 𝕜 f s x := by + rw [gradientWithin, (toDual 𝕜 F).apply_symm_apply] + +@[simp] +lemma toDual_gradient : (toDual 𝕜 F) (∇ f x) = fderiv 𝕜 f x := by + rw [gradient, (toDual 𝕜 F).apply_symm_apply] + +@[simp] +lemma toDual_comp_gradientWithin : + (toDual 𝕜 F) ∘ gradientWithin f s = fderivWithin 𝕜 f s := + funext fun _ => toDual_gradientWithin + +@[simp] +lemma toDual_comp_gradient : (toDual 𝕜 F) ∘ ∇ f = fderiv 𝕜 f := + funext fun _ => toDual_gradient + theorem HasGradientAt.unique {gradf gradg : F} (hf : HasGradientAt f gradf x) (hg : HasGradientAt f gradg x) : gradf = gradg := @@ -125,8 +143,7 @@ theorem HasGradientAt.unique {gradf gradg : F} theorem DifferentiableAt.hasGradientAt (h : DifferentiableAt 𝕜 f x) : HasGradientAt f (∇ f x) x := by - rw [hasGradientAt_iff_hasFDerivAt, gradient, (toDual 𝕜 F).apply_symm_apply (fderiv 𝕜 f x)] - exact h.hasFDerivAt + simpa [hasGradientAt_iff_hasFDerivAt] using h.hasFDerivAt theorem HasGradientAt.differentiableAt (h : HasGradientAt f f' x) : DifferentiableAt 𝕜 f x := @@ -134,9 +151,7 @@ theorem HasGradientAt.differentiableAt (h : HasGradientAt f f' x) : theorem DifferentiableWithinAt.hasGradientWithinAt (h : DifferentiableWithinAt 𝕜 f s x) : HasGradientWithinAt f (gradientWithin f s x) s x := by - rw [hasGradientWithinAt_iff_hasFDerivWithinAt, gradientWithin, - (toDual 𝕜 F).apply_symm_apply (fderivWithin 𝕜 f s x)] - exact h.hasFDerivWithinAt + simpa [hasGradientWithinAt_iff_hasFDerivWithinAt] using h.hasFDerivWithinAt theorem HasGradientWithinAt.differentiableWithinAt (h : HasGradientWithinAt f f' s x) : DifferentiableWithinAt 𝕜 f s x := diff --git a/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean b/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean index 18b864f5d1edd5..aff008563104c5 100644 --- a/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean +++ b/Mathlib/Analysis/Calculus/IteratedDeriv/Lemmas.lean @@ -75,8 +75,7 @@ theorem iteratedDerivWithin_add (hf : ContDiffWithinAt 𝕜 n f s x) (hg : ContDiffWithinAt 𝕜 n g s x) : iteratedDerivWithin n (f + g) s x = iteratedDerivWithin n f s x + iteratedDerivWithin n g s x := by - simp_rw [iteratedDerivWithin, iteratedFDerivWithin_add_apply hf hg h hx, - ContinuousMultilinearMap.add_apply] + simp_rw [iteratedDerivWithin, iteratedFDerivWithin_add_apply hf hg h hx, add_apply] include h hx in theorem iteratedDerivWithin_fun_add @@ -103,9 +102,7 @@ theorem iteratedDerivWithin_const_sub (hn : 0 < n) (c : F) : include h hx in theorem iteratedDerivWithin_const_smul (c : R) (hf : ContDiffWithinAt 𝕜 n f s x) : iteratedDerivWithin n (c • f) s x = c • iteratedDerivWithin n f s x := by - simp_rw [iteratedDerivWithin] - rw [iteratedFDerivWithin_const_smul_apply (a := c) hf h hx] - simp only [ContinuousMultilinearMap.smul_apply] + simp [iteratedDerivWithin, iteratedFDerivWithin_const_smul_apply hf h hx] include h hx in theorem iteratedDerivWithin_fun_const_smul (c : R) (hf : ContDiffWithinAt 𝕜 n f s x) : diff --git a/Mathlib/Analysis/Calculus/Monotone.lean b/Mathlib/Analysis/Calculus/Monotone.lean index 30c72dc98563c3..76a4396b7ee376 100644 --- a/Mathlib/Analysis/Calculus/Monotone.lean +++ b/Mathlib/Analysis/Calculus/Monotone.lean @@ -38,8 +38,6 @@ open Set Filter Function Metric MeasureTheory MeasureTheory.Measure IsUnifLocDou open scoped Topology --- see https://github.com/leanprover-community/mathlib4/issues/29041 -set_option linter.unusedSimpArgs false in /-- If `(f y - f x) / (y - x)` converges to a limit as `y` tends to `x`, then the same goes if `y` is shifted a little bit, i.e., `f (y + (y-x)^2) - f x) / (y - x)` converges to the same limit. This lemma contains a slightly more general version of this statement (where one considers @@ -62,7 +60,7 @@ theorem tendsto_apply_add_mul_sq_div_sub {f : ℝ → ℝ} {x a c d : ℝ} {l : apply Tendsto.congr' _ Z have : ∀ᶠ y in l, y + c * (y - x) ^ 2 ≠ x := by apply Tendsto.mono_right h' hl self_mem_nhdsWithin filter_upwards [this] with y hy - simp [field, sub_ne_zero.2 hy] + simp [field] /-- A Stieltjes function is almost everywhere differentiable, with derivative equal to the Radon-Nikodym derivative of the associated Stieltjes measure with respect to Lebesgue. -/ diff --git a/Mathlib/Analysis/Calculus/Rademacher.lean b/Mathlib/Analysis/Calculus/Rademacher.lean index d582d301482c81..fb1a4980548a2d 100644 --- a/Mathlib/Analysis/Calculus/Rademacher.lean +++ b/Mathlib/Analysis/Calculus/Rademacher.lean @@ -109,7 +109,7 @@ theorem integral_inv_smul_sub_mul_tendsto_integral_lineDeriv_mul apply tendsto_integral_filter_of_dominated_convergence (fun x ↦ (C * ‖v‖) * ‖g x‖) · filter_upwards with t apply AEStronglyMeasurable.mul ?_ hg.aestronglyMeasurable - apply aestronglyMeasurable_const.smul + apply aestronglyMeasurable_const.fun_smul apply AEStronglyMeasurable.sub _ hf.continuous.measurable.aestronglyMeasurable apply AEMeasurable.aestronglyMeasurable exact hf.continuous.measurable.comp_aemeasurable' (aemeasurable_id'.add_const _) @@ -134,7 +134,7 @@ theorem integral_inv_smul_sub_mul_tendsto_integral_lineDeriv_mul' (K.indicator (fun x ↦ (C * ‖v‖) * ‖g x‖)) · filter_upwards with t apply AEStronglyMeasurable.mul ?_ hg.aestronglyMeasurable - apply aestronglyMeasurable_const.smul + apply aestronglyMeasurable_const.fun_smul apply AEStronglyMeasurable.sub _ hf.continuous.measurable.aestronglyMeasurable apply AEMeasurable.aestronglyMeasurable exact hf.continuous.measurable.comp_aemeasurable' (aemeasurable_id'.add_const _) diff --git a/Mathlib/Analysis/Calculus/TangentCone/Real.lean b/Mathlib/Analysis/Calculus/TangentCone/Real.lean index 1f389e2985e4c9..cae58175fd1a14 100644 --- a/Mathlib/Analysis/Calculus/TangentCone/Real.lean +++ b/Mathlib/Analysis/Calculus/TangentCone/Real.lean @@ -99,6 +99,9 @@ theorem uniqueDiffOn_Iio (a : ℝ) : UniqueDiffOn ℝ (Iio a) := theorem uniqueDiffOn_Icc {a b : ℝ} (hab : a < b) : UniqueDiffOn ℝ (Icc a b) := uniqueDiffOn_convex (convex_Icc a b) <| by simp only [interior_Icc, nonempty_Ioo, hab] +theorem uniqueDiffOn_uIcc {a b : ℝ} (hab : a ≠ b) : UniqueDiffOn ℝ (uIcc a b) := + uniqueDiffOn_Icc <| min_lt_max.mpr hab + theorem uniqueDiffOn_Ico (a b : ℝ) : UniqueDiffOn ℝ (Ico a b) := if hab : a < b then uniqueDiffOn_convex (convex_Ico a b) <| by simp only [interior_Ico, nonempty_Ioo, hab] diff --git a/Mathlib/Analysis/Calculus/TaylorIntegral.lean b/Mathlib/Analysis/Calculus/TaylorIntegral.lean new file mode 100644 index 00000000000000..cfec073db45c76 --- /dev/null +++ b/Mathlib/Analysis/Calculus/TaylorIntegral.lean @@ -0,0 +1,139 @@ +/- +Copyright (c) 2025 Moritz Doll. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Moritz Doll +-/ +module + +public import Mathlib.MeasureTheory.Integral.IntervalIntegral.IntegrationByParts +public import Mathlib.Analysis.Calculus.ContDiff.Basic +public import Mathlib.Analysis.Calculus.Deriv.Pow +public import Mathlib.Analysis.Calculus.IteratedDeriv.Defs + +/-! +# Taylor's formula with an integral remainder in higher dimensions + +In this file we prove Taylor's formula with the remainder term in integral form. + +* `map_add_eq_sum_add_integral_iteratedFDeriv`: version for higher dimensions with `iteratedFDeriv` + +TODO: add a version that assumes `ContDiffOn f (closedBall x (‖y‖))` + +-/ + +@[expose] public section + +open Nat + +variable {𝕜 E F : Type*} +variable [NormedAddCommGroup E] [NormedAddCommGroup F] + +section NontriviallyNormedField + +variable [NontriviallyNormedField 𝕜] [NormedSpace 𝕜 E] [NormedSpace 𝕜 F] + +variable {f : E → F} {x y : E} {t : 𝕜} {n : ℕ} + +theorem DifferentiableAt.deriv_comp_add_smul (hf : DifferentiableAt 𝕜 f (x + t • y)) : + deriv (fun (s : 𝕜) ↦ f (x + s • y)) t = fderiv 𝕜 f (x + t • y) y := by + have hg : Differentiable 𝕜 (fun (s : 𝕜) ↦ (x + s • y)) := by fun_prop + convert fderiv_comp_deriv t hf hg.differentiableAt + · simp + · simpa using (deriv_smul_const (x := t) differentiableAt_id y).symm + +theorem ContDiffAt.deriv_fderiv_add_smul (hf : ContDiffAt 𝕜 (n + 1) f (x + t • y)) : + deriv (fun (s : 𝕜) ↦ iteratedFDeriv 𝕜 n f (x + s • y) (fun _ ↦ y)) t = + iteratedFDeriv 𝕜 (n + 1) f (x + t • y) (fun _ ↦ y) := by + have hf' : DifferentiableAt 𝕜 (iteratedFDeriv 𝕜 n f) (x + t • y) := by + apply hf.differentiableAt_iteratedFDeriv + norm_cast + exact lt_add_one n + convert (hf'.continuousMultilinear_apply_const _).deriv_comp_add_smul + exact hf'.iteratedFDeriv_succ_apply_left' + +end NontriviallyNormedField + +variable [NormedSpace ℝ E] [NormedSpace ℝ F] + +variable {f : E → F} {x y : E} {n : ℕ} + +variable [CompleteSpace F] + +/-- *Taylor's theorem with remainder in integral form*. If `f` is `n + 1` times continuously +differentiable, then `f (x + y)` is given by +`∑ k in 0..n, D^k f(x; y,..,y) / k! + 1/n! ∫ t in 0..1, (1 - t) ^ n • D^{n+1}f (x + t • y; y,..,y)`, +where `D^k f` denotes the iterated derivative of `f`. + +In the case that `n = 1`, this is a reformulation of the fundamental theorem of calculus, namely +`f (x + y) = f x + ∫ t in 0..1, D f(x + t • y; y)`. -/ +theorem map_add_eq_sum_add_integral_iteratedFDeriv (hf : ∀ (t : ℝ) (_ht : t ∈ Set.Icc 0 1), + ContDiffAt ℝ (n + 1) f (x + t • y)) : + f (x + y) = ∑ k ∈ Finset.range (n + 1), (k ! : ℝ)⁻¹ • (iteratedFDeriv ℝ k f x (fun _ ↦ y)) + + (n ! : ℝ)⁻¹ • ∫ t in 0..1, (1 - t)^n • iteratedFDeriv ℝ (n + 1) f (x + t • y) (fun _ ↦ y) := by + simp_rw [← Set.uIcc_of_le zero_le_one] at hf + induction n with + | zero => + -- The base case follows from the fundamental theorem of calculus + have h_eq : Set.EqOn (fun t ↦ (fderiv ℝ f (x + t • y)) y) (deriv fun (s : ℝ) ↦ f (x + s • y)) + (Set.uIcc 0 1) := by + intro t ht + rw [DifferentiableAt.deriv_comp_add_smul] + exact (hf t ht).differentiableAt (by simp) + simp only [zero_add, Finset.range_one, Finset.sum_singleton, factorial_zero, cast_one, inv_one, + iteratedFDeriv_zero_apply, one_smul, pow_zero, reduceAdd, iteratedFDeriv_one_apply] + rw [← sub_eq_iff_eq_add', Eq.comm, intervalIntegral.integral_congr h_eq] + have hf' : ∀ (t : ℝ) (ht : t ∈ Set.uIcc 0 1), DifferentiableAt ℝ (fun s ↦ f (x + s • y)) t := + fun t ht ↦ ((hf t ht).differentiableAt (by simp)).comp t (by fun_prop) + have hint : IntervalIntegrable (deriv (fun s ↦ f (x + s • y))) MeasureTheory.volume 0 1 := by + have h₁ : ContinuousOn (fderiv ℝ f) ((fun t ↦ x + t • y) '' Set.uIcc (0 : ℝ) 1) := by + intro z ⟨t, ht, hz⟩ + rw [← hz] + exact (((hf t ht).fderiv_right (le_refl _)).continuousAt (n := 0)).continuousWithinAt + have h₂ : ContinuousOn (fun x_1 ↦ fderiv ℝ f (x + x_1 • y)) (Set.uIcc (0 : ℝ) 1) := by + apply h₁.comp (t := (fun t ↦ x + t • y) '' (Set.uIcc (0 : ℝ) 1)) (by fun_prop) + intro t ht + use t + apply (ContinuousOn.congr _ h_eq.symm).intervalIntegrable + fun_prop + simpa using intervalIntegral.integral_deriv_eq_sub hf' hint + | succ n ih => + -- We use the inductive hypothesis to cancel all lower order terms + specialize ih (fun t ht ↦ (hf t ht).of_le (by simp)) + rw [Finset.sum_range_succ, add_assoc] + convert ih using 2 + -- We define the functions u and v that we will integrate by parts + set u := fun (k : ℕ) (t : ℝ) ↦ (k ! : ℝ)⁻¹ * (1 - t) ^ k + have hu : ∀ (t : ℝ), HasDerivAt (u (n + 1)) (-u n t) t := by + intro t + unfold u + have : (-((n ! : ℝ)⁻¹ * (1 - t) ^ n)) = + ((n + 1) ! : ℝ)⁻¹ * ((n + 1) * (1 - t) ^ n * (-1)) := by + field_simp + congr 1 + rw [Nat.factorial_succ] + grind + rw [this] + apply HasDerivAt.const_mul + convert ((hasDerivAt_id t).const_sub 1).pow (n + 1) + all_goals norm_cast + have hu' : Continuous (u n) := by fun_prop + set v := fun (k : ℕ) (t : ℝ) ↦ iteratedFDeriv ℝ k f (x + t • y) (fun _ ↦ y) + have hv : ∀ (t : ℝ) (ht : t ∈ Set.uIcc 0 1), HasDerivAt (v (n + 1)) (v (n + 1 + 1) t) t := by + intro t ht + unfold v + rw [← (hf t ht).deriv_fderiv_add_smul] + have h_diff : DifferentiableAt ℝ (iteratedFDeriv ℝ (n + 1) f) (x + t • y) := by + apply (hf t ht).differentiableAt_iteratedFDeriv + norm_cast + grind + refine DifferentiableAt.hasDerivAt ?_ + fun_prop + have hv' : ContinuousOn (v (n + 1 + 1)) (Set.uIcc 0 1) := by + intro t ht + have h_cont : ContinuousAt (iteratedFDeriv ℝ (n + 1 + 1) f) (x + t • y) := + ((hf t ht).iteratedFDeriv_right (i := n + 1 + 1) (m := 0) (by simp)).continuousAt + exact (h_cont.comp (x := t) (by fun_prop)).continuousWithinAt.eval_const _ + -- Now we apply integration by parts and simplify + simpa [← eq_neg_add_iff_add_eq, ← intervalIntegral.integral_smul, smul_smul, u, v] using + intervalIntegral.integral_smul_deriv_eq_deriv_smul (fun t _ ↦ hu t) hv + (hu'.neg.intervalIntegrable _ _) hv'.intervalIntegrable diff --git a/Mathlib/Analysis/Complex/Basic.lean b/Mathlib/Analysis/Complex/Basic.lean index 6e4accd7174eea..be3b9d791a5c2b 100644 --- a/Mathlib/Analysis/Complex/Basic.lean +++ b/Mathlib/Analysis/Complex/Basic.lean @@ -93,7 +93,7 @@ instance (priority := 900) _root_.NormedAlgebra.complexToReal {A : Type*} [Semin @[continuity, fun_prop] theorem continuous_normSq : Continuous normSq := by - simpa [← Complex.normSq_eq_norm_sq] using continuous_norm (E := ℂ).pow 2 + simpa [← Complex.normSq_eq_norm_sq] using continuous_norm (E := ℂ).fun_pow 2 theorem nnnorm_eq_one_of_pow_eq_one {ζ : ℂ} {n : ℕ} (h : ζ ^ n = 1) (hn : n ≠ 0) : ‖ζ‖₊ = 1 := (pow_left_inj₀ zero_le zero_le hn).1 <| by rw [← nnnorm_pow, h, nnnorm_one, one_pow] diff --git a/Mathlib/Analysis/Complex/BranchLogRoot.lean b/Mathlib/Analysis/Complex/BranchLogRoot.lean index 2a00d8bd917320..ffcf8ac4c78979 100644 --- a/Mathlib/Analysis/Complex/BranchLogRoot.lean +++ b/Mathlib/Analysis/Complex/BranchLogRoot.lean @@ -5,7 +5,7 @@ Authors: Yury Kudryashov -/ module -public import Mathlib.Topology.Connected.LocPathConnected +public import Mathlib.Topology.Connected.LocallyPathConnected public import Mathlib.Analysis.Complex.Basic public import Mathlib.AlgebraicTopology.FundamentalGroupoid.SimplyConnected public import Mathlib.Analysis.Complex.Exponential @@ -27,7 +27,7 @@ open Set namespace Complex -variable {X : Type*} [TopologicalSpace X] [LocPathConnectedSpace X] {U : Set X} +variable {X : Type*} [TopologicalSpace X] [LocallyPathConnectedSpace X] {U : Set X} /-- If `g : X → ℂ` defined on a locally path connected space is continuous on an open simply connected set `U` and `0 ∉ g '' U`, @@ -39,7 +39,7 @@ theorem exists_continuousOn_eqOn_exp_comp (hUc : IsSimplyConnected U) (hUo : IsO ∃ f : X → ℂ, ContinuousOn f U ∧ EqOn (exp ∘ f) g U := by classical have := hUc.simplyConnectedSpace - have := hUo.locPathConnectedSpace + have := hUo.locallyPathConnectedSpace rcases hUc.nonempty with ⟨x₀, hx₀U⟩ have hx₀ : g x₀ ≠ 0 := ne_of_mem_of_not_mem (mem_image_of_mem g hx₀U) hU₀ lift x₀ to U using hx₀U diff --git a/Mathlib/Analysis/Complex/CauchyIntegral.lean b/Mathlib/Analysis/Complex/CauchyIntegral.lean index 8dbb1d187028aa..7ab35a8eefd071 100644 --- a/Mathlib/Analysis/Complex/CauchyIntegral.lean +++ b/Mathlib/Analysis/Complex/CauchyIntegral.lean @@ -656,6 +656,11 @@ protected theorem _root_.Differentiable.contDiff ContDiff ℂ n f := contDiff_iff_contDiffAt.mpr fun z ↦ (hf.analyticAt z).contDiffAt +@[fun_prop] +theorem _root_.Differentiable.deriv {f : ℂ → E} (hf : Differentiable ℂ f) : + Differentiable ℂ (deriv f) := + hf.contDiff.differentiable_deriv_two + /-- When `f : ℂ → E` is differentiable, the `cauchyPowerSeries f z R` represents `f` as a power series centered at `z` in the entirety of `ℂ`, regardless of `R : ℝ≥0`, with `0 < R`. -/ protected theorem _root_.Differentiable.hasFPowerSeriesOnBall {f : ℂ → E} (h : Differentiable ℂ f) diff --git a/Mathlib/Analysis/Complex/CoveringMap.lean b/Mathlib/Analysis/Complex/CoveringMap.lean index 95872f8372379f..2d5b9fa2902f01 100644 --- a/Mathlib/Analysis/Complex/CoveringMap.lean +++ b/Mathlib/Analysis/Complex/CoveringMap.lean @@ -53,7 +53,7 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] [ProperSpace 𝕜] theorem Polynomial.isCoveringMapOn_eval (p : 𝕜[X]) : IsCoveringMapOn p.eval (p.eval '' {k | p.derivative.eval k = 0})ᶜ := by - refine p.isClosedMap_eval.isCoveringMapOn_of_openPartialHomeomorph (fun x hx ↦ ?_) + refine p.isClosedMap_eval.isCoveringMapOn_of_isLocalHomeomorphOn (fun x hx ↦ ?_) fun x hx ↦ ⟨_, ((p.hasStrictDerivAt x).hasStrictFDerivAt_equiv fun h ↦ hx ⟨x, h, rfl⟩).mem_toOpenPartialHomeomorph_source, by simp⟩ obtain rfl | ne := eq_or_ne p (C x) diff --git a/Mathlib/Analysis/Complex/Exponential.lean b/Mathlib/Analysis/Complex/Exponential.lean index 5d95d2378a9389..1bf61e0671053f 100644 --- a/Mathlib/Analysis/Complex/Exponential.lean +++ b/Mathlib/Analysis/Complex/Exponential.lean @@ -692,10 +692,10 @@ open Lean.Meta Qq /-- Extension for the `positivity` tactic: `Real.exp` is always positive. -/ @[positivity Real.exp _] -meta def evalExp : PositivityExt where eval {u α} _ pα? e := do +meta def evalExp : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Real.exp $a) => - let some _ := pα? | pure .none assertInstancesCommute pure (.positive q(Real.exp_pos $a)) | _, _, _ => throwError "not Real.exp" diff --git a/Mathlib/Analysis/Complex/Harmonic/Liouville.lean b/Mathlib/Analysis/Complex/Harmonic/Liouville.lean index 4440e10c032cda..bd948e355b8688 100644 --- a/Mathlib/Analysis/Complex/Harmonic/Liouville.lean +++ b/Mathlib/Analysis/Complex/Harmonic/Liouville.lean @@ -7,6 +7,7 @@ module public import Mathlib.Analysis.Complex.Liouville public import Mathlib.Analysis.Complex.Harmonic.Analytic +public import Mathlib.Analysis.Normed.Module.HahnBanach /-! # Liouville's Theorem for Harmonic Functions on the Complex Plane diff --git a/Mathlib/Analysis/Complex/Harmonic/Poisson.lean b/Mathlib/Analysis/Complex/Harmonic/Poisson.lean index dff01e6642c121..23f6f29bbb0d2f 100644 --- a/Mathlib/Analysis/Complex/Harmonic/Poisson.lean +++ b/Mathlib/Analysis/Complex/Harmonic/Poisson.lean @@ -60,7 +60,7 @@ theorem HarmonicOnNhd.circleAverage_re_herglotzRieszKernel_smul · apply h₂F grind [mem_ball] -- CircleIntegrable (fun z ↦ ((z - c + (w - c)) / (z - c - (w - c))).re • F z) c R - apply (ContinuousOn.smul _ _).circleIntegrable' + apply (ContinuousOn.fun_smul _ _).circleIntegrable' · apply (continuousOn_herglotz_riesz hw).mono grind [mem_ball, dist_eq_norm, mem_sphere_iff_norm, (pos_of_mem_ball hw)] · apply (h₁F.mono _).continuousOn (𝕜 := ℂ) diff --git a/Mathlib/Analysis/Complex/LocallyUniformLimit.lean b/Mathlib/Analysis/Complex/LocallyUniformLimit.lean index 85b8521479cb74..dfc0c7acfda663 100644 --- a/Mathlib/Analysis/Complex/LocallyUniformLimit.lean +++ b/Mathlib/Analysis/Complex/LocallyUniformLimit.lean @@ -65,13 +65,14 @@ theorem norm_cderiv_le (hr : 0 < r) (hf : ∀ w ∈ sphere z r, ‖f w‖ ≤ M) theorem cderiv_sub (hr : 0 < r) (hf : ContinuousOn f (sphere z r)) (hg : ContinuousOn g (sphere z r)) : cderiv r (f - g) z = cderiv r f z - cderiv r g z := by have h1 : ContinuousOn (fun w : ℂ => ((w - z) ^ 2)⁻¹) (sphere z r) := by - refine ((continuous_id'.sub continuous_const).pow 2).continuousOn.inv₀ fun w hw h => hr.ne ?_ + refine ((continuous_id'.fun_sub continuous_const).fun_pow 2).continuousOn.inv₀ + fun w hw h => hr.ne ?_ rwa [mem_sphere_iff_norm, sq_eq_zero_iff.mp h, norm_zero] at hw simp_rw [cderiv, ← smul_sub] congr 1 simpa only [Pi.sub_apply, smul_sub] using - circleIntegral.integral_sub ((h1.smul hf).circleIntegrable hr.le) - ((h1.smul hg).circleIntegrable hr.le) + circleIntegral.integral_sub ((h1.fun_smul hf).circleIntegrable hr.le) + ((h1.fun_smul hg).circleIntegrable hr.le) theorem norm_cderiv_lt (hr : 0 < r) (hfM : ∀ w ∈ sphere z r, ‖f w‖ < M) (hf : ContinuousOn f (sphere z r)) : ‖cderiv r f z‖ < M / r := by diff --git a/Mathlib/Analysis/Complex/Order.lean b/Mathlib/Analysis/Complex/Order.lean index 03bf6ffb6cb7fa..43dc270e625bde 100644 --- a/Mathlib/Analysis/Complex/Order.lean +++ b/Mathlib/Analysis/Complex/Order.lean @@ -142,10 +142,10 @@ alias ⟨_, ofReal_ne_zero_of_ne_zero⟩ := ofReal_ne_zero /-- Extension for the `positivity` tactic: `Complex.ofReal` is positive/nonnegative/nonzero if its input is. -/ @[positivity Complex.ofReal _, Complex.ofReal _] -meta def evalComplexOfReal : PositivityExt where eval {u α} _ pα? e := do +meta def evalComplexOfReal : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℂ), ~q(Complex.ofReal $a) => - let some _ := pα? | pure .none assumeInstancesCommute match ← core q(inferInstance) (some q(inferInstance)) a with | .positive pa => return .positive q(ofReal_pos $pa) diff --git a/Mathlib/Analysis/Complex/Periodic.lean b/Mathlib/Analysis/Complex/Periodic.lean index 4cb853d1ba5833..ee74a523d80fab 100644 --- a/Mathlib/Analysis/Complex/Periodic.lean +++ b/Mathlib/Analysis/Complex/Periodic.lean @@ -74,6 +74,9 @@ theorem norm_qParam_lt_iff (hh : 0 < h) (A : ℝ) (z : ℂ) : rw [norm_qParam, Real.exp_lt_exp, div_lt_div_iff_of_pos_right hh, mul_lt_mul_left_of_neg] simpa using Real.pi_pos +theorem norm_qParam_lt_one (hh : 0 < h) {z : ℂ} (hz : 0 < im z) : ‖𝕢 h z‖ < 1 := by + simpa using (norm_qParam_lt_iff hh 0 z).mpr hz + lemma qParam_ne_zero (z : ℂ) : 𝕢 h z ≠ 0 := by simp [qParam, exp_ne_zero] diff --git a/Mathlib/Analysis/Complex/RealDeriv.lean b/Mathlib/Analysis/Complex/RealDeriv.lean index c9888b6c0f21ce..dbf10e546853b9 100644 --- a/Mathlib/Analysis/Complex/RealDeriv.lean +++ b/Mathlib/Analysis/Complex/RealDeriv.lean @@ -109,4 +109,36 @@ theorem HasDerivWithinAt.ofReal_comp {f : ℝ → ℝ} {s : Set ℝ} {u : ℝ} simpa only [Function.comp_apply, ofRealCLM_apply] using! ofRealCLM.hasFDerivAt.comp_hasDerivWithinAt z hf +@[fun_prop] +lemma Complex.differentiable_re : Differentiable ℝ Complex.re := reCLM.differentiable + +@[fun_prop] +lemma Complex.differentiable_im : Differentiable ℝ Complex.im := imCLM.differentiable + +@[fun_prop] +lemma Complex.differentiable_ofReal : Differentiable ℝ Complex.ofReal := ofRealCLM.differentiable + +open ComplexConjugate in +@[fun_prop] +lemma Complex.differentiable_conj : Differentiable ℝ (conj : ℂ → ℂ) := conjCLE.differentiable + +variable {f : ℂ → E} {s : Set ℂ} {z : ℂ} + +@[fun_prop] +lemma Differentiable.real_of_complex (hf : Differentiable ℂ f) : Differentiable ℝ f := + hf.restrictScalars (𝕜 := ℝ) + +@[fun_prop] +lemma DifferentiableAt.real_of_complex (hf : DifferentiableAt ℂ f z) : DifferentiableAt ℝ f z := + hf.restrictScalars (𝕜 := ℝ) + +@[fun_prop] +lemma DifferentiableWithinAt.real_of_complex (hf : DifferentiableWithinAt ℂ f s z) : + DifferentiableWithinAt ℝ f s z := + hf.restrictScalars (𝕜 := ℝ) + +@[fun_prop] +lemma DifferentiableOn.real_of_complex (hf : DifferentiableOn ℂ f s) : DifferentiableOn ℝ f s := + hf.restrictScalars (𝕜 := ℝ) + end RealDerivOfComplex diff --git a/Mathlib/Analysis/Complex/Schwarz.lean b/Mathlib/Analysis/Complex/Schwarz.lean index 930b181880faae..075078ce6ae7c6 100644 --- a/Mathlib/Analysis/Complex/Schwarz.lean +++ b/Mathlib/Analysis/Complex/Schwarz.lean @@ -7,6 +7,7 @@ module public import Mathlib.Analysis.Complex.AbsMax public import Mathlib.Analysis.Complex.RemovableSingularity +public import Mathlib.Analysis.Normed.Module.HahnBanach /-! # Schwarz lemma diff --git a/Mathlib/Analysis/Complex/Trigonometric.lean b/Mathlib/Analysis/Complex/Trigonometric.lean index 44d62339fd1e11..554e4153002295 100644 --- a/Mathlib/Analysis/Complex/Trigonometric.lean +++ b/Mathlib/Analysis/Complex/Trigonometric.lean @@ -940,10 +940,10 @@ open Lean.Meta Qq /-- Extension for the `positivity` tactic: `Real.cosh` is always positive. -/ @[positivity Real.cosh _] -meta def evalCosh : PositivityExt where eval {u α} _ pα? e := do +meta def evalCosh : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Real.cosh $a) => - let some _ := pα? | pure .none assertInstancesCommute return .positive q(Real.cosh_pos $a) | _, _, _ => throwError "not Real.cosh" diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean index 1ce4914b6dffdb..6786ec87928768 100644 --- a/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean +++ b/Mathlib/Analysis/Complex/UpperHalfPlane/Basic.lean @@ -148,20 +148,20 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: `UpperHalfPlane.im`. -/ @[positivity UpperHalfPlane.im _] -meta def evalUpperHalfPlaneIm : PositivityExt where eval {u α} _zα pα? e := do +meta def evalUpperHalfPlaneIm : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(UpperHalfPlane.im $a) => - let some _ := pα? | pure .none assertInstancesCommute pure (.positive q(@UpperHalfPlane.im_pos $a)) | _, _, _ => throwError "not UpperHalfPlane.im" /-- Extension for the `positivity` tactic: `UpperHalfPlane.coe`. -/ @[positivity UpperHalfPlane.coe _] -meta def evalUpperHalfPlaneCoe : PositivityExt where eval {u α} _zα pα? e := do +meta def evalUpperHalfPlaneCoe : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℂ), ~q(UpperHalfPlane.coe $a) => - let some _ := pα? | pure .none assertInstancesCommute pure (.nonzero q(@UpperHalfPlane.ne_zero $a)) | _, _, _ => throwError "not UpperHalfPlane.coe" diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean index 2585e242b97ebe..44e3e2b3ed77f9 100644 --- a/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean +++ b/Mathlib/Analysis/Complex/UpperHalfPlane/Manifold.lean @@ -158,6 +158,20 @@ TODO(MR): investigate if using `mvfderiv` can avoid the "pain" above, and be a c section Complex +/-- Derivative of `z ↦ (denom g z) ^ k`: $\frac{d}{dz}[(cz+d)^k] = k \cdot c \cdot (cz+d)^{k-1}$. -/ +lemma hasDerivAt_denom_zpow (g : GL (Fin 2) ℝ) (k : ℤ) (τ : ℍ) : + HasDerivAt (fun z ↦ denom g z ^ k) (k * g 1 0 * denom g τ ^ (k - 1)) τ := by + have hd : HasDerivAt (denom g ·) (g 1 0) τ := by + simpa [denom] using hasDerivAt_id _ |>.const_mul _ |>.add_const (g 1 1 : ℂ) + have := (hasDerivAt_zpow k (denom g τ) (Or.inl (denom_ne_zero g τ))).comp _ hd + simpa only [Function.comp_def, mul_right_comm] using this + +/-- Derivative of `z ↦ (denom g z) ^ k`: +$\frac{d}{dz}[(cz+d)^k] = k \cdot c \cdot (cz+d)^{k-1}$. -/ +lemma deriv_denom_zpow (g : GL (Fin 2) ℝ) (k : ℤ) (τ : ℍ) : + deriv (fun z ↦ denom g z ^ k) τ = k * g 1 0 * denom g τ ^ (k - 1) := + (hasDerivAt_denom_zpow g k τ).deriv + lemma hasStrictDerivAt_smul {g : GL (Fin 2) ℝ} (hg : 0 < g.val.det) (τ : ℍ) : HasStrictDerivAt (fun z ↦ ↑(g • ofComplex z) : ℂ → ℂ) (g.val.det / denom g τ ^ 2) τ := by suffices HasStrictDerivAt (num g / denom g) (g.val.det / denom g τ ^ 2) τ by diff --git a/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean b/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean index 5962cb0757487a..fa7163e6fe288f 100644 --- a/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean +++ b/Mathlib/Analysis/Complex/UpperHalfPlane/Topology.lean @@ -69,7 +69,7 @@ instance : ContractibleSpace ℍ := by rw [isEmbedding_coe.toHomeomorph.trans (.setCongr range_coe) |>.contractibleSpace_iff] exact (convex_halfSpace_im_gt 0).contractibleSpace ⟨I, one_pos.trans_eq I_im.symm⟩ -instance : LocPathConnectedSpace ℍ := isOpenEmbedding_coe.locPathConnectedSpace +instance : LocallyPathConnectedSpace ℍ := isOpenEmbedding_coe.locallyPathConnectedSpace instance : NoncompactSpace ℍ where noncompact_univ h := by diff --git a/Mathlib/Analysis/Complex/ValueDistribution/Cartan.lean b/Mathlib/Analysis/Complex/ValueDistribution/Cartan.lean index 06ec0547d43ab6..1c44cf7fabd4a0 100644 --- a/Mathlib/Analysis/Complex/ValueDistribution/Cartan.lean +++ b/Mathlib/Analysis/Complex/ValueDistribution/Cartan.lean @@ -6,7 +6,8 @@ Authors: Matteo Cipollina, Stefan Kebekus module -public import Mathlib.Analysis.SpecialFunctions.Integrals.PosLogEqCircleAverage +public import Mathlib.Analysis.Complex.ValueDistribution.FirstMainTheorem +public import Mathlib.Analysis.Complex.ValueDistribution.Proximity.IntegralPresentation /-! # Cartan's Formula @@ -27,19 +28,13 @@ At present, this file establishes circle integrability of the function See Section VI.2 of [Lang, *Introduction to Complex Hyperbolic Spaces*][MR886677] for a detailed discussion. - - -## TODO - -- Establish Cartan's Formula in full -- Prove monotonicity of the characteristic function -/ public section open Filter Metric Real Set Topology -variable {f : ℂ → ℂ} +variable {f : ℂ → ℂ} {R : ℝ} namespace ValueDistribution @@ -136,4 +131,91 @@ theorem circleAverage_log_norm_meromorphicTrailingCoeffAt_of_meromorphicOrderAt_ rw [meromorphicOrderAt_const] aesop +/- Specialized Jensen-type identity -/ +private lemma logCounting_add_log_trailingCoeff_eq_circleAverage_add_logCounting_top + (h : Meromorphic f) (hR : R ≠ 0) (a : ℂ) : + logCounting f a R + log ‖meromorphicTrailingCoeffAt (f · - a) 0‖ = + circleAverage (log ‖f · - a‖) 0 R + logCounting f ⊤ R := by + have : logCounting f a R - logCounting f ⊤ R = circleAverage (log ‖f · - a‖) 0 R + - log ‖meromorphicTrailingCoeffAt (f · - a) 0‖ := by + rw [logCounting_coe_eq_logCounting_sub_const_zero, ← logCounting_sub_const h] + exact logCounting_zero_sub_logCounting_top_eq_circleAverage_sub_const (by fun_prop) hR + linarith + +/-- +Circle integrability of the term `logCounting f · R` that appears in Cartan's formula. +-/ +theorem circleIntegrable_logCounting (h : Meromorphic f) : + CircleIntegrable (logCounting f · R) 0 1 := by + by_cases hR : R = 0 + · simp [hR, ValueDistribution.logCounting_eval_zero] + convert circleIntegrable_circleAverage_log_norm_sub h |>.add + (circleIntegrable_const (logCounting f ⊤ R) 0 1) |>.sub + circleIntegrable_log_meromorphicTrailingCoeffAt + simpa using eq_sub_of_add_eq + (logCounting_add_log_trailingCoeff_eq_circleAverage_add_logCounting_top h hR _) + +/-! +## Cartan's formula +-/ + +/-- +**Cartan's formula** with the additive constant written explicitly as a circle average of the +logarithm of the first nonzero Laurent coefficient of `f - a` at the origin. + +See `circleIntegrable_logCounting` and `circleIntegrable_log_trailingCoeff_of_meromorphic` for the +facts that the summands are actually circle integrable. +-/ +theorem characteristic_top_eq_circleAverage_add_circleAverage (h : Meromorphic f) (hR : R ≠ 0) : + characteristic f ⊤ R = circleAverage (logCounting f · R) 0 1 + + circleAverage (fun a ↦ log ‖meromorphicTrailingCoeffAt (f · - a) 0‖) 0 1 := calc + characteristic f ⊤ R + = circleAverage (fun a ↦ circleAverage (log ‖f · - a‖) 0 R + logCounting f ⊤ R) 0 1 := by + simp only [characteristic, proximity, ↓reduceDIte, Pi.add_apply] + rw [← proximity_top, ← circleAverage_circleAverage_eq_proximity_top h, + circleAverage_fun_add (circleIntegrable_circleAverage_log_norm_sub h) + (circleIntegrable_const (logCounting f ⊤ R) 0 1), circleAverage_const] + _ = circleAverage (logCounting f · R) 0 1 + + circleAverage (fun a ↦ log ‖meromorphicTrailingCoeffAt (f · - a) 0‖) 0 1 := by + rw [← circleAverage_add (circleIntegrable_logCounting h) + circleIntegrable_log_meromorphicTrailingCoeffAt, circleAverage_congr_sphere] + intro a ha + simp [logCounting_add_log_trailingCoeff_eq_circleAverage_add_logCounting_top h hR a] + +/-- +**Cartan's formula** in case where `0 < meromorphicOrderAt f 0`. +-/ +theorem characteristic_top_eq_circleAverage_of_meromorphicOrderAt_pos + (h₁f : Meromorphic f) (h₂f : 0 < meromorphicOrderAt f 0) (hR : R ≠ 0) : + characteristic f ⊤ R = circleAverage (logCounting f · R) 0 1 := by + rw [characteristic_top_eq_circleAverage_add_circleAverage h₁f hR] + simp [circleAverage_log_norm_meromorphicTrailingCoeffAt_of_meromorphicOrderAt_pos h₂f] + +/-- +Qualitative version of **Cartan's formula**: Away from the point `0`, the difference between +`characteristic f ⊤` and `circleAverage (logCounting f · ·) 0 1` is constant. This qualitative +version of Cartan's formula exists because the specific value of the constant does not matter in +practise. +-/ +theorem characteristic_top_eq_circleAverage_add_const (h : Meromorphic f) : + ∃ const, ∀ R ≠ 0, characteristic f ⊤ R = circleAverage (logCounting f · R) 0 1 + const := + ⟨circleAverage (fun a ↦ log ‖meromorphicTrailingCoeffAt (f · - a) 0‖) 0 1, + fun _ hr ↦ characteristic_top_eq_circleAverage_add_circleAverage h hr⟩ + +/-! +## Application: Monotonicity of the Characteristic Function +-/ + +/-- +The characteristic function is monotone on `(0, ∞)`. This result is surprisingly non-trivial, given +that the proximity function is not monotone in general. +-/ +theorem characteristic_monotoneOn (h : Meromorphic f) : + MonotoneOn (characteristic f ⊤) (Set.Ioi 0) := by + intro a ha b hb hab + rw [characteristic_top_eq_circleAverage_add_circleAverage h ha.ne', + characteristic_top_eq_circleAverage_add_circleAverage h hb.ne'] + gcongr <;> try exact circleIntegrable_logCounting h + exact logCounting_monotoneOn ha hb hab + end ValueDistribution diff --git a/Mathlib/Analysis/Convex/BetweenList.lean b/Mathlib/Analysis/Convex/BetweenList.lean index abe92b3744fc36..a25c94db1e5dc7 100644 --- a/Mathlib/Analysis/Convex/BetweenList.lean +++ b/Mathlib/Analysis/Convex/BetweenList.lean @@ -180,20 +180,9 @@ variable [Field R] [LinearOrder R] [IsStrictOrderedRing R] variable {R} lemma SortedLE.wbtw {l : List R} (h : l.SortedLE) : l.Wbtw R := by - induction l with - | nil => simp - | cons head tail ih => - rw [wbtw_cons] - refine ⟨?_, ih h.pairwise.of_cons.sortedLE⟩ - clear ih - induction tail with - | nil => simp - | cons head' tail' ih => - rw [pairwise_cons] - refine ⟨?_, ih (h.pairwise.sublist ?_).sortedLE⟩ - · simp_rw [sortedLE_iff_pairwise, pairwise_cons_cons, pairwise_cons] at h - exact fun a ha ↦ .of_le_of_le h.1 (h.2.2.1 a ha) - · simp + rw [List.Wbtw, List.triplewise_iff_getElem] + intro i j k hij hjk hk + exact Wbtw.of_le_of_le (h.getElem_le_getElem_of_le hij.le) (h.getElem_le_getElem_of_le hjk.le) lemma SortedLT.sbtw {l : List R} (h : l.SortedLT) : l.Sbtw R := ⟨h.sortedLE.wbtw, h.nodup⟩ diff --git a/Mathlib/Analysis/Convex/Cone/Closure.lean b/Mathlib/Analysis/Convex/Cone/Closure.lean index 2d1e5ffe4a67cf..d7edaf16a33634 100644 --- a/Mathlib/Analysis/Convex/Cone/Closure.lean +++ b/Mathlib/Analysis/Convex/Cone/Closure.lean @@ -60,8 +60,11 @@ lemma toConvexCone_closure_pointed (K : PointedCone 𝕜 E) : (K : ConvexCone /-- The closure of a pointed cone inside a topological space as a pointed cone. This construction is mainly used for defining maps between proper cones. -/ -protected def closure (K : PointedCone 𝕜 E) : PointedCone 𝕜 E := - K.toConvexCone.closure.toPointedCone K.toConvexCone_closure_pointed +protected def closure (K : PointedCone 𝕜 E) : PointedCone 𝕜 E where + carrier := closure ↑K + zero_mem' := subset_closure (zero_mem K) + smul_mem' c _ h₁ := map_mem_closure (continuous_const_smul c.1) h₁ fun _ h₂ ↦ K.smul_mem c.2 h₂ + add_mem' h₁ h₂ := map_mem_closure₂ continuous_add h₁ h₂ (fun _ ha _ hb ↦ K.add_mem ha hb) @[simp, norm_cast] theorem coe_closure (K : PointedCone 𝕜 E) : (K.closure : Set E) = closure K := diff --git a/Mathlib/Analysis/Convex/Cone/Extension.lean b/Mathlib/Analysis/Convex/Cone/Extension.lean index 0cb73ec103fc24..234e879eb8eb5a 100644 --- a/Mathlib/Analysis/Convex/Cone/Extension.lean +++ b/Mathlib/Analysis/Convex/Cone/Extension.lean @@ -6,7 +6,7 @@ Authors: Yury Kudryashov, Frédéric Dupuis module public import Mathlib.Algebra.Order.Archimedean.Real.Basic -public import Mathlib.Geometry.Convex.Cone.Basic +public import Mathlib.Geometry.Convex.Cone.Pointed public import Mathlib.LinearAlgebra.LinearPMap /-! @@ -56,7 +56,7 @@ namespace RieszExtension open Submodule -variable (s : ConvexCone ℝ E) (f : E →ₗ.[ℝ] ℝ) +variable (s : PointedCone ℝ E) (f : E →ₗ.[ℝ] ℝ) /-- Induction step in M. Riesz extension theorem. Given a convex cone `s` in a vector space `E`, a partially defined linear map `f : f.domain → ℝ`, assume that `f` is nonnegative on `f.domain ∩ p` @@ -142,7 +142,7 @@ end RieszExtension and a linear `f : p → ℝ`, assume that `f` is nonnegative on `p ∩ s` and `p + s = E`. Then there exists a globally defined linear function `g : E → ℝ` that agrees with `f` on `p`, and is nonnegative on `s`. -/ -theorem riesz_extension (s : ConvexCone ℝ E) (f : E →ₗ.[ℝ] ℝ) +theorem riesz_extension (s : PointedCone ℝ E) (f : E →ₗ.[ℝ] ℝ) (nonneg : ∀ x : f.domain, (x : E) ∈ s → 0 ≤ f x) (dense : ∀ y, ∃ x : f.domain, (x : E) + y ∈ s) : ∃ g : E →ₗ[ℝ] ℝ, (∀ x : f.domain, g x = f x) ∧ ∀ x ∈ s, 0 ≤ g x := by @@ -160,21 +160,18 @@ theorem exists_extension_of_le_sublinear (f : E →ₗ.[ℝ] ℝ) (N : E → ℝ (N_hom : ∀ c : ℝ, 0 < c → ∀ x, N (c • x) = c * N x) (N_add : ∀ x y, N (x + y) ≤ N x + N y) (hf : ∀ x : f.domain, f x ≤ N x) : ∃ g : E →ₗ[ℝ] ℝ, (∀ x : f.domain, g x = f x) ∧ ∀ x, g x ≤ N x := by - let s : ConvexCone ℝ (E × ℝ) := + have N_0 : N 0 = 0 := by grind [N_hom 2 (by norm_num) 0, smul_zero] + let s : PointedCone ℝ (E × ℝ) := { carrier := { p : E × ℝ | N p.1 ≤ p.2 } - smul_mem' := fun c hc p hp => - calc - N (c • p.1) = c * N p.1 := N_hom c hc p.1 - _ ≤ c * p.2 := by gcongr; exact hp - add_mem' := fun x hx y hy => (N_add _ _).trans (add_le_add hx hy) } + zero_mem' := by simp [N_0] + smul_mem' := fun ⟨_, hc⟩ _ _ => by rcases eq_or_lt_of_le' hc <;> simp_all + add_mem' := fun hx hy => (N_add _ _).trans (add_le_add hx hy) } set f' := (-f).coprod (LinearMap.id.toPMap ⊤) have hf'_nonneg : ∀ x : f'.domain, x.1 ∈ s → 0 ≤ f' x := fun x (hx : N x.1.1 ≤ x.1.2) ↦ by simpa [f'] using le_trans (hf ⟨x.1.1, x.2.1⟩) hx have hf'_dense : ∀ y : E × ℝ, ∃ x : f'.domain, ↑x + y ∈ s := by rintro ⟨x, y⟩ - refine ⟨⟨(0, N x - y), ⟨f.domain.zero_mem, trivial⟩⟩, ?_⟩ - simp only [s, ConvexCone.mem_mk, mem_setOf_eq, Prod.fst_add, Prod.snd_add, zero_add, - sub_add_cancel, le_rfl] + exact ⟨⟨(0, N x - y), ⟨f.domain.zero_mem, trivial⟩⟩, by simp [s]⟩ obtain ⟨g, g_eq, g_nonneg⟩ := riesz_extension s f' hf'_nonneg hf'_dense replace g_eq : ∀ (x : f.domain) (y : ℝ), g (x, y) = y - f x := fun x y ↦ (g_eq ⟨(x, y), ⟨x.2, trivial⟩⟩).trans (sub_eq_neg_add _ _).symm diff --git a/Mathlib/Analysis/Convex/Gauge.lean b/Mathlib/Analysis/Convex/Gauge.lean index 6e2eea7f3179a9..f9f9a4716a3155 100644 --- a/Mathlib/Analysis/Convex/Gauge.lean +++ b/Mathlib/Analysis/Convex/Gauge.lean @@ -67,7 +67,7 @@ theorem gauge_def' : gauge s x = sInf {r ∈ Set.Ioi (0 : ℝ) | r⁻¹ • x congrm sInf {r | ?_} exact and_congr_right fun hr => mem_smul_set_iff_inv_smul_mem₀ hr.ne' _ _ -private theorem gauge_set_bddBelow : BddBelow { r : ℝ | 0 < r ∧ x ∈ r • s } := +private theorem bddBelow_gauge_set : BddBelow { r : ℝ | 0 < r ∧ x ∈ r • s } := ⟨0, fun _ hr => hr.1.le⟩ /-- If the given subset is `Absorbent` then the set we take an infimum over in `gauge` is nonempty, @@ -79,7 +79,7 @@ theorem Absorbent.gauge_set_nonempty (absorbs : Absorbent ℝ s) : theorem gauge_mono (hs : Absorbent ℝ s) (h : s ⊆ t) : gauge t ≤ gauge s := fun _ => by unfold gauge - gcongr; exacts [gauge_set_bddBelow, hs.gauge_set_nonempty] + gcongr; exacts [bddBelow_gauge_set, hs.gauge_set_nonempty] theorem exists_lt_of_gauge_lt (absorbs : Absorbent ℝ s) (h : gauge s x < a) : ∃ b, 0 < b ∧ b < a ∧ x ∈ b • s := by @@ -131,10 +131,10 @@ theorem gauge_neg_set_eq_gauge_neg (x : E) : gauge (-s) x = gauge s (-x) := by theorem gauge_le_of_mem (ha : 0 ≤ a) (hx : x ∈ a • s) : gauge s x ≤ a := by obtain rfl | ha' := ha.eq_or_lt · rw [mem_singleton_iff.1 (zero_smul_set_subset _ hx), gauge_zero] - · exact csInf_le gauge_set_bddBelow ⟨ha', hx⟩ + · exact csInf_le bddBelow_gauge_set ⟨ha', hx⟩ -theorem gauge_le_eq (hs₁ : Convex ℝ s) (hs₀ : (0 : E) ∈ s) (hs₂ : Absorbent ℝ s) (ha : 0 ≤ a) : - { x | gauge s x ≤ a } = ⋂ (r : ℝ) (_ : a < r), r • s := by +theorem setOf_gauge_le_eq (hs₁ : Convex ℝ s) (hs₀ : (0 : E) ∈ s) (hs₂ : Absorbent ℝ s) + (ha : 0 ≤ a) : { x | gauge s x ≤ a } = ⋂ (r : ℝ) (_ : a < r), r • s := by ext x simp_rw [Set.mem_iInter, Set.mem_setOf_eq] refine ⟨fun h r hr => ?_, fun h => le_of_forall_pos_lt_add fun ε hε => ?_⟩ @@ -148,7 +148,9 @@ theorem gauge_le_eq (hs₁ : Convex ℝ s) (hs₀ : (0 : E) ∈ s) (hs₂ : Abso exact hδr.le · linarith [gauge_le_of_mem (by linarith) <| h (a + ε / 2) (by linarith)] -theorem gauge_lt_eq' (absorbs : Absorbent ℝ s) (a : ℝ) : +@[deprecated (since := "2026-06-17")] alias gauge_le_eq := setOf_gauge_le_eq + +theorem setOf_gauge_lt_eq' (absorbs : Absorbent ℝ s) (a : ℝ) : { x | gauge s x < a } = ⋃ (r : ℝ) (_ : 0 < r) (_ : r < a), r • s := by ext simp_rw [mem_setOf, mem_iUnion, exists_prop] @@ -156,7 +158,9 @@ theorem gauge_lt_eq' (absorbs : Absorbent ℝ s) (a : ℝ) : ⟨exists_lt_of_gauge_lt absorbs, fun ⟨r, hr₀, hr₁, hx⟩ => (gauge_le_of_mem hr₀.le hx).trans_lt hr₁⟩ -theorem gauge_lt_eq (absorbs : Absorbent ℝ s) (a : ℝ) : +@[deprecated (since := "2026-06-17")] alias gauge_lt_eq' := setOf_gauge_lt_eq' + +theorem setOf_gauge_lt_eq (absorbs : Absorbent ℝ s) (a : ℝ) : { x | gauge s x < a } = ⋃ r ∈ Set.Ioo 0 (a : ℝ), r • s := by ext simp_rw [mem_setOf, mem_iUnion, exists_prop, mem_Ioo, and_assoc] @@ -164,17 +168,22 @@ theorem gauge_lt_eq (absorbs : Absorbent ℝ s) (a : ℝ) : ⟨exists_lt_of_gauge_lt absorbs, fun ⟨r, hr₀, hr₁, hx⟩ => (gauge_le_of_mem hr₀.le hx).trans_lt hr₁⟩ +@[deprecated (since := "2026-06-17")] alias gauge_lt_eq := setOf_gauge_lt_eq + theorem mem_openSegment_of_gauge_lt_one (absorbs : Absorbent ℝ s) (hgauge : gauge s x < 1) : ∃ y ∈ s, x ∈ openSegment ℝ 0 y := by rcases exists_lt_of_gauge_lt absorbs hgauge with ⟨r, hr₀, hr₁, y, hy, rfl⟩ refine ⟨y, hy, 1 - r, r, ?_⟩ simp [*] -theorem gauge_lt_one_subset_self (hs : Convex ℝ s) (h₀ : (0 : E) ∈ s) (absorbs : Absorbent ℝ s) : - { x | gauge s x < 1 } ⊆ s := fun _x hx ↦ +theorem setOf_gauge_lt_one_subset_self (hs : Convex ℝ s) (h₀ : (0 : E) ∈ s) + (absorbs : Absorbent ℝ s) : { x | gauge s x < 1 } ⊆ s := fun _x hx ↦ let ⟨_y, hys, hx⟩ := mem_openSegment_of_gauge_lt_one absorbs hx hs.openSegment_subset h₀ hys hx +@[deprecated (since := "2026-06-17")] +alias gauge_lt_one_subset_self := setOf_gauge_lt_one_subset_self + theorem gauge_le_one_of_mem {x : E} (hx : x ∈ s) : gauge s x ≤ 1 := gauge_le_of_mem zero_le_one <| by rwa [one_smul] @@ -196,19 +205,20 @@ theorem gauge_sum_le {ι : Type*} (hs : Convex ℝ s) (absorbs : Absorbent ℝ s (f : ι → E) : gauge s (∑ i ∈ t, f i) ≤ ∑ i ∈ t, gauge s (f i) := Finset.le_sum_of_subadditive _ gauge_zero.le (gauge_add_le hs absorbs) _ _ -theorem self_subset_gauge_le_one : s ⊆ { x | gauge s x ≤ 1 } := fun _ => gauge_le_one_of_mem +theorem self_subset_setOf_gauge_le_one : s ⊆ { x | gauge s x ≤ 1 } := fun _ => gauge_le_one_of_mem -theorem Convex.gauge_le (hs : Convex ℝ s) (h₀ : (0 : E) ∈ s) (absorbs : Absorbent ℝ s) (a : ℝ) : - Convex ℝ { x | gauge s x ≤ a } := by +@[deprecated (since := "2026-06-17")] +alias self_subset_gauge_le_one := self_subset_setOf_gauge_le_one + +theorem Convex.setOf_gauge_le (hs : Convex ℝ s) (h₀ : (0 : E) ∈ s) (absorbs : Absorbent ℝ s) + (a : ℝ) : Convex ℝ { x | gauge s x ≤ a } := by by_cases ha : 0 ≤ a - · rw [gauge_le_eq hs h₀ absorbs ha] + · rw [setOf_gauge_le_eq hs h₀ absorbs ha] exact convex_iInter fun i => convex_iInter fun _ => hs.smul _ · convert! convex_empty (𝕜 := ℝ) exact eq_empty_iff_forall_notMem.2 fun x hx => ha <| (gauge_nonneg _).trans hx -theorem Balanced.starConvex (hs : Balanced ℝ s) : StarConvex ℝ 0 s := - starConvex_zero_iff.2 fun _ hx a ha₀ ha₁ => - hs _ (by rwa [Real.norm_of_nonneg ha₀]) (smul_mem_smul_set hx) +@[deprecated (since := "2026-06-17")] alias Convex.gauge_le := Convex.setOf_gauge_le theorem le_gauge_of_notMem (hs₀ : StarConvex ℝ 0 s) (hs₂ : Absorbs ℝ s {x}) (hx : x ∉ a • s) : a ≤ gauge s x := by @@ -357,12 +367,16 @@ theorem interior_subset_gauge_lt_one (s : Set E) : interior s ⊆ { x | gauge s rcases H₂.exists with ⟨r, hxr, hr₀, hr₁⟩ exact (gauge_le_of_mem hr₀.le hxr).trans_lt hr₁ -theorem gauge_lt_one_eq_self_of_isOpen (hs₁ : Convex ℝ s) (hs₀ : (0 : E) ∈ s) (hs₂ : IsOpen s) : - { x | gauge s x < 1 } = s := by - refine (gauge_lt_one_subset_self hs₁ ‹_› <| absorbent_nhds_zero <| hs₂.mem_nhds hs₀).antisymm ?_ +theorem setOf_gauge_lt_one_eq_self_of_isOpen (hs₁ : Convex ℝ s) (hs₀ : (0 : E) ∈ s) + (hs₂ : IsOpen s) : { x | gauge s x < 1 } = s := by + refine (setOf_gauge_lt_one_subset_self hs₁ ‹_› <| absorbent_nhds_zero <| + hs₂.mem_nhds hs₀).antisymm ?_ convert! interior_subset_gauge_lt_one s exact hs₂.interior_eq.symm +@[deprecated (since := "2026-06-17")] +alias gauge_lt_one_eq_self_of_isOpen := setOf_gauge_lt_one_eq_self_of_isOpen + theorem gauge_lt_one_of_mem_of_isOpen (hs₂ : IsOpen s) {x : E} (hx : x ∈ s) : gauge s x < 1 := interior_subset_gauge_lt_one s <| by rwa [hs₂.interior_eq] @@ -378,7 +392,7 @@ theorem mem_closure_of_gauge_le_one (hc : Convex ℝ s) (hs₀ : 0 ∈ s) (ha : (h : gauge s x ≤ 1) : x ∈ closure s := by have : ∀ᶠ r : ℝ in 𝓝[<] 1, r • x ∈ s := by filter_upwards [Ico_mem_nhdsLT one_pos] with r ⟨hr₀, hr₁⟩ - apply gauge_lt_one_subset_self hc hs₀ ha + apply setOf_gauge_lt_one_subset_self hc hs₀ ha rw [mem_setOf_eq, gauge_smul_of_nonneg hr₀] exact mul_lt_one_of_nonneg_of_lt_one_left hr₀ hr₁ h refine mem_closure_of_tendsto ?_ this @@ -446,15 +460,18 @@ is continuous. If the ambient space is a normed space, then `gauge s` is Lipschi theorem continuous_gauge (hc : Convex ℝ s) (hs₀ : s ∈ 𝓝 0) : Continuous (gauge s) := continuous_iff_continuousAt.2 fun _ ↦ continuousAt_gauge hc hs₀ -theorem gauge_lt_one_eq_interior (hc : Convex ℝ s) (hs₀ : s ∈ 𝓝 0) : +theorem setOf_gauge_lt_one_eq_interior (hc : Convex ℝ s) (hs₀ : s ∈ 𝓝 0) : { x | gauge s x < 1 } = interior s := by refine Subset.antisymm (fun x hx ↦ ?_) (interior_subset_gauge_lt_one s) rcases mem_openSegment_of_gauge_lt_one (absorbent_nhds_zero hs₀) hx with ⟨y, hys, hxy⟩ exact hc.openSegment_interior_self_subset_interior (mem_interior_iff_mem_nhds.2 hs₀) hys hxy +@[deprecated (since := "2026-06-17")] +alias gauge_lt_one_eq_interior := setOf_gauge_lt_one_eq_interior + theorem gauge_lt_one_iff_mem_interior (hc : Convex ℝ s) (hs₀ : s ∈ 𝓝 0) : gauge s x < 1 ↔ x ∈ interior s := - Set.ext_iff.1 (gauge_lt_one_eq_interior hc hs₀) _ + Set.ext_iff.1 (setOf_gauge_lt_one_eq_interior hc hs₀) _ theorem gauge_le_one_iff_mem_closure (hc : Convex ℝ s) (hs₀ : s ∈ 𝓝 0) : gauge s x ≤ 1 ↔ x ∈ closure s := @@ -487,7 +504,7 @@ theorem gaugeSeminorm_lt_one_of_isOpen (hs : IsOpen s) {x : E} (hx : x ∈ s) : theorem gaugeSeminorm_ball_one (hs : IsOpen s) : (gaugeSeminorm hs₀ hs₁ hs₂).ball 0 1 = s := by rw [Seminorm.ball_zero_eq] - exact gauge_lt_one_eq_self_of_isOpen hs₁ hs₂.zero_mem hs + exact setOf_gauge_lt_one_eq_self_of_isOpen hs₁ hs₂.zero_mem hs end RCLike diff --git a/Mathlib/Analysis/Convex/MetricSpace.lean b/Mathlib/Analysis/Convex/MetricSpace.lean index f9926781d7352f..848ed2f4e6d82d 100644 --- a/Mathlib/Analysis/Convex/MetricSpace.lean +++ b/Mathlib/Analysis/Convex/MetricSpace.lean @@ -240,7 +240,7 @@ lemma continuous_convexCombPair_of_isBounded (add_sub_cancel ..) (x t) (y j)), dist_convexCombPair_convexCombPair_le] simp only [dist_self, mul_zero, add_zero, dist_convexCombPair_left] grw [abs_sub_comm, ← le_abs_self] at hj' - grw [hj, hj', hf1, hD] + grw [hj.le, hj'.le, hf1, hD] · field_simp; norm_num · exact hf0 _ diff --git a/Mathlib/Analysis/Convex/Side.lean b/Mathlib/Analysis/Convex/Side.lean index 000cf596517e4f..6cf3ee3d020915 100644 --- a/Mathlib/Analysis/Convex/Side.lean +++ b/Mathlib/Analysis/Convex/Side.lean @@ -347,6 +347,46 @@ theorem _root_.Wbtw.wOppSide₃₁ {s : AffineSubspace R P} {x y z : P} (h : Wbt end StrictOrderedCommRing +section LinearOrderedCommRing + +variable [CommRing R] [LinearOrder R] [IsStrictOrderedRing R] + [AddCommGroup V] [Module R V] [AddTorsor V P] + +/-- If `x` and `y` are displaced from points of `s` by multiples of a common vector whose +coefficients have nonnegative product, they are weakly on the same side of `s`. -/ +theorem wSameSide_of_vsub_eq_smul {s : AffineSubspace R P} {x y p₁ p₂ : P} {m : V} {c₁ c₂ : R} + (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) (h₁ : x -ᵥ p₁ = c₁ • m) (h₂ : y -ᵥ p₂ = c₂ • m) + (hc : 0 ≤ c₁ * c₂) : s.WSameSide x y := by + refine ⟨p₁, hp₁, p₂, hp₂, ?_⟩ + rw [h₁, h₂] + exact sameRay_smul_smul_of_mul_nonneg hc + +/-- If `x` and `y` are displaced from points of `s` by multiples of a common vector whose +coefficients have nonpositive product, they are weakly on opposite sides of `s`. -/ +theorem wOppSide_of_vsub_eq_smul {s : AffineSubspace R P} {x y p₁ p₂ : P} {m : V} {c₁ c₂ : R} + (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) (h₁ : x -ᵥ p₁ = c₁ • m) (h₂ : y -ᵥ p₂ = c₂ • m) + (hc : c₁ * c₂ ≤ 0) : s.WOppSide x y := by + refine ⟨p₁, hp₁, p₂, hp₂, ?_⟩ + have h₂' : p₂ -ᵥ y = (-c₂) • m := by rw [← neg_vsub_eq_vsub_rev, h₂, neg_smul] + rw [h₁, h₂'] + exact sameRay_smul_smul_of_mul_nonneg (by rw [mul_neg]; exact neg_nonneg.2 hc) + +/-- If `x` and `y` lie off `s` and are displaced from points of `s` by multiples of a common +vector whose coefficients have nonnegative product, they are strictly on the same side of `s`. -/ +theorem sSameSide_of_vsub_eq_smul {s : AffineSubspace R P} {x y p₁ p₂ : P} {m : V} {c₁ c₂ : R} + (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) (h₁ : x -ᵥ p₁ = c₁ • m) (h₂ : y -ᵥ p₂ = c₂ • m) + (hc : 0 ≤ c₁ * c₂) (hx : x ∉ s) (hy : y ∉ s) : s.SSameSide x y := + ⟨wSameSide_of_vsub_eq_smul hp₁ hp₂ h₁ h₂ hc, hx, hy⟩ + +/-- If `x` and `y` lie off `s` and are displaced from points of `s` by multiples of a common +vector whose coefficients have nonpositive product, they are strictly on opposite sides of `s`. -/ +theorem sOppSide_of_vsub_eq_smul {s : AffineSubspace R P} {x y p₁ p₂ : P} {m : V} {c₁ c₂ : R} + (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) (h₁ : x -ᵥ p₁ = c₁ • m) (h₂ : y -ᵥ p₂ = c₂ • m) + (hc : c₁ * c₂ ≤ 0) (hx : x ∉ s) (hy : y ∉ s) : s.SOppSide x y := + ⟨wOppSide_of_vsub_eq_smul hp₁ hp₂ h₁ h₂ hc, hx, hy⟩ + +end LinearOrderedCommRing + section LinearOrderedField variable [Field R] [LinearOrder R] [IsStrictOrderedRing R] diff --git a/Mathlib/Analysis/Convex/StdSimplex.lean b/Mathlib/Analysis/Convex/StdSimplex.lean index 9836e956e90d08..46b59101d93b89 100644 --- a/Mathlib/Analysis/Convex/StdSimplex.lean +++ b/Mathlib/Analysis/Convex/StdSimplex.lean @@ -87,17 +87,13 @@ theorem ite_eq_mem_stdSimplex (i : ι) : (if i = · then (1 : 𝕜) else 0) ∈ variable [IsOrderedRing 𝕜] set_option linter.overlappingInstances false - -#adaptation_note /-- nightly-2024-03-11 -we need a type annotation on the segment in the following two lemmas. -/ - /-- The edges are contained in the simplex. -/ lemma segment_single_subset_stdSimplex (i j : ι) : - ([Pi.single i 1 -[𝕜] Pi.single j 1] : Set (ι → 𝕜)) ⊆ stdSimplex 𝕜 ι := + [Pi.single i 1 -[𝕜] Pi.single j 1] ⊆ stdSimplex 𝕜 ι := (convex_stdSimplex 𝕜 ι).segment_subset (single_mem_stdSimplex _ _) (single_mem_stdSimplex _ _) lemma stdSimplex_fin_two : - stdSimplex 𝕜 (Fin 2) = ([Pi.single 0 1 -[𝕜] Pi.single 1 1] : Set (Fin 2 → 𝕜)) := by + stdSimplex 𝕜 (Fin 2) = [Pi.single 0 1 -[𝕜] Pi.single 1 1] := by refine Subset.antisymm ?_ (segment_single_subset_stdSimplex 𝕜 (0 : Fin 2) 1) rintro f ⟨hf₀, hf₁⟩ rw [Fin.sum_univ_two] at hf₁ diff --git a/Mathlib/Analysis/Convex/StrictCombination.lean b/Mathlib/Analysis/Convex/StrictCombination.lean index 7466e912b7b343..d0b40eae1ca35d 100644 --- a/Mathlib/Analysis/Convex/StrictCombination.lean +++ b/Mathlib/Analysis/Convex/StrictCombination.lean @@ -56,9 +56,7 @@ lemma StrictConvex.centerMass_mem_interior {s : Set V} {t : Finset ι} {w : ι · have hwi : w i + ∑ j ∈ t, w j ≠ 0 := by refine LT.lt.ne' ?_ have hwi : 0 < w i := by grind - grw [hwi] - simp only [lt_add_iff_pos_right] - exact (sum_nonneg hs₀).lt_of_ne' hsum_t + grw [← hwi, ← sum_nonneg hs₀, add_zero] simp only [hzi, ← add_smul, ← add_div, ne_eq, hwi, not_false_eq_true, div_self, one_smul] by_cases! hijt : ∃ i'' j'', i'' ∈ t ∧ j'' ∈ t ∧ z i'' ≠ z j'' ∧ w i'' ≠ 0 ∧ w j'' ≠ 0 · grind diff --git a/Mathlib/Analysis/Convolution.lean b/Mathlib/Analysis/Convolution.lean index b35fb5ee876b75..2f7b10b145e3f6 100644 --- a/Mathlib/Analysis/Convolution.lean +++ b/Mathlib/Analysis/Convolution.lean @@ -807,8 +807,8 @@ theorem convolution_tendsto_right {ι} {g : ι → G → E'} {l : Filter ι} {x have hgi : dist (g i (k i)) z₀ < ε / 3 := hgδ hpi (hki.trans <| half_lt_self hδ) have h1 : ∀ x' ∈ ball (k i) (δ / 2), dist (g i x') (g i (k i)) ≤ ε / 3 + ε / 3 := by intro x' hx' - refine (dist_triangle_right _ _ _).trans (add_le_add (hgδ hpi ?_).le hgi.le) - exact ((dist_triangle _ _ _).trans_lt (add_lt_add hx'.out hki)).trans_eq (add_halves δ) + grw [dist_triangle_right, hgδ hpi ?_, hgi] + grw [dist_triangle, hx'.out, hki, add_halves] have := dist_convolution_le (add_pos h2ε h2ε).le hφi hnφi hiφi hmgi h1 refine ((dist_triangle _ _ _).trans_lt (add_lt_add_of_le_of_lt this hgi)).trans_eq ?_ ring @@ -946,7 +946,7 @@ noncomputable def posConvolution (f : ℝ → E) (g : ℝ → E') (L : E →L[ indicator (Ioi (0 : ℝ)) fun x => ∫ t in 0..x, L (f t) (g (x - t)) ∂ν theorem posConvolution_eq_convolution_indicator (f : ℝ → E) (g : ℝ → E') (L : E →L[ℝ] E' →L[ℝ] F) - (ν : Measure ℝ := by volume_tac) [NoAtoms ν] : + (ν : Measure ℝ := by volume_tac) [NullSingletonClass ν] : posConvolution f g L ν = convolution (indicator (Ioi 0) f) (indicator (Ioi 0) g) L ν := by ext1 x rw [convolution, posConvolution, indicator] @@ -974,7 +974,7 @@ theorem posConvolution_eq_convolution_indicator (f : ℝ → E) (g : ℝ → E') · rw [indicator_of_notMem (mem_Ioi.not.mpr ht), map_zero, zero_apply] theorem integrable_posConvolution {f : ℝ → E} {g : ℝ → E'} {μ ν : Measure ℝ} [SFinite μ] - [SFinite ν] [IsAddRightInvariant μ] [NoAtoms ν] (hf : IntegrableOn f (Ioi 0) ν) + [SFinite ν] [IsAddRightInvariant μ] [NullSingletonClass ν] (hf : IntegrableOn f (Ioi 0) ν) (hg : IntegrableOn g (Ioi 0) μ) (L : E →L[ℝ] E' →L[ℝ] F) : Integrable (posConvolution f g L ν) μ := by rw [← integrable_indicator_iff (measurableSet_Ioi : MeasurableSet (Ioi (0 : ℝ)))] at hf hg @@ -985,7 +985,7 @@ theorem integrable_posConvolution {f : ℝ → E} {g : ℝ → E'} {μ ν : Meas of their integrals over this set. (Compare `integral_convolution` for the two-sided convolution.) -/ theorem integral_posConvolution [CompleteSpace E] [CompleteSpace E'] [CompleteSpace F] {μ ν : Measure ℝ} - [SFinite μ] [SFinite ν] [IsAddRightInvariant μ] [NoAtoms ν] {f : ℝ → E} {g : ℝ → E'} + [SFinite μ] [SFinite ν] [IsAddRightInvariant μ] [NullSingletonClass ν] {f : ℝ → E} {g : ℝ → E'} (hf : IntegrableOn f (Ioi 0) ν) (hg : IntegrableOn g (Ioi 0) μ) (L : E →L[ℝ] E' →L[ℝ] F) : ∫ x : ℝ in Ioi 0, ∫ t : ℝ in 0..x, L (f t) (g (x - t)) ∂ν ∂μ = L (∫ x : ℝ in Ioi 0, f x ∂ν) (∫ x : ℝ in Ioi 0, g x ∂μ) := by diff --git a/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean b/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean index d3a90374eea707..0ccb13f47c2c04 100644 --- a/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean +++ b/Mathlib/Analysis/Distribution/ContDiffMapSupportedIn.lean @@ -191,7 +191,7 @@ theorem copy_eq (f : 𝓓^{n}_{K}(E, F)) (f' : E → F) (h : f' = f) : f.copy f' @[simp] theorem coe_toBoundedContinuousFunction (f : 𝓓^{n}_{K}(E, F)) : - (f : BoundedContinuousFunction E F) = (f : E → F) := rfl + (f : BoundedContinuousFunction E F) = (f : E → F) := rfl section AddCommGroup @@ -234,7 +234,7 @@ instance : IsSubApply 𝓓^{n}_{K}(E, F) E F where @[deprecated (since := "2026-06-15")] alias coe_sub := FunLike.coe_sub instance instSMul {R} [Semiring R] [Module R F] [SMulCommClass ℝ R F] [ContinuousConstSMul R F] : - SMul R 𝓓^{n}_{K}(E, F) where + SMul R 𝓓^{n}_{K}(E, F) where smul c f := .mk (c • (f : E → F)) (f.contDiff.const_smul c) <| by rw [← smul_zero c] exact f.zero_on_compl.comp_left diff --git a/Mathlib/Analysis/Distribution/Distribution.lean b/Mathlib/Analysis/Distribution/Distribution.lean index a9731fcfaa4774..141a771d758926 100644 --- a/Mathlib/Analysis/Distribution/Distribution.lean +++ b/Mathlib/Analysis/Distribution/Distribution.lean @@ -259,6 +259,11 @@ lemma lineDerivOp_eq_lineDerivCLM {v : E} {T : 𝓓'(Ω, F)} : ∂_{v} T = lineDerivCLM v T := rfl +@[simp] +theorem lineDerivOp_apply_apply (f : 𝓓'(Ω, F)) (g : 𝓓(Ω, ℝ)) (m : E) : + ∂_{m} f g = f (- ∂_{m} g) := by + rw [map_neg]; rfl + noncomputable instance : LineDerivAdd E 𝓓'(Ω, F) 𝓓'(Ω, F) where lineDerivOp_add v := map_add (lineDerivCLM v) lineDerivOp_left_add _ _ T := congr($lineDerivCLM_add T) diff --git a/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean b/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean index bbd069ea784a13..6246742ddac1b8 100644 --- a/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean +++ b/Mathlib/Analysis/Distribution/SchwartzSpace/Basic.lean @@ -1202,6 +1202,15 @@ theorem toBoundedContinuousFunctionCLM_apply (f : 𝓢(E, F)) (x : E) : toBoundedContinuousFunctionCLM 𝕜 E F f x = f x := rfl +theorem toBoundedContinuousFunctionCLM_injective : + Function.Injective (toBoundedContinuousFunctionCLM .. : 𝓢(E, F) →L[𝕜] E →ᵇ F) := + fun _ _ h ↦ DFunLike.ext _ _ fun x ↦ DFunLike.congr_fun h x + +instance : T3Space 𝓢(E, F) := + suffices T2Space 𝓢(E, F) from inferInstance + .of_injective_continuous (toBoundedContinuousFunctionCLM_injective ℝ ..) + (ContinuousLinearMap.continuous _) + end BoundedContinuousFunction section ZeroAtInfty @@ -1312,6 +1321,10 @@ theorem memLp (f : 𝓢(E, F)) (p : ℝ≥0∞) (μ : Measure E := by volume_tac def toLp (f : 𝓢(E, F)) (p : ℝ≥0∞) (μ : Measure E := by volume_tac) [hμ : μ.HasTemperateGrowth] : Lp F p μ := (f.memLp p μ).toLp +instance instCoeToLp {p : ℝ≥0∞} {μ : Measure E} [hμ : μ.HasTemperateGrowth] : + Coe 𝓢(E, F) (Lp F p μ) where + coe := (SchwartzMap.toLp · p μ) + theorem coeFn_toLp (f : 𝓢(E, F)) (p : ℝ≥0∞) (μ : Measure E := by volume_tac) [hμ : μ.HasTemperateGrowth] : f.toLp p μ =ᵐ[μ] f := (f.memLp p μ).coeFn_toLp diff --git a/Mathlib/Analysis/Distribution/Support.lean b/Mathlib/Analysis/Distribution/Support.lean index b1e1c0da3d5c20..1598e7ae0ae86b 100644 --- a/Mathlib/Analysis/Distribution/Support.lean +++ b/Mathlib/Analysis/Distribution/Support.lean @@ -6,6 +6,7 @@ Authors: Moritz Doll, Anatole Dedecker module public import Mathlib.Analysis.Distribution.TemperedDistribution +public import Mathlib.Analysis.Distribution.Distribution /-! # Support of distributions @@ -24,7 +25,8 @@ compactly supported) and all basic properties are proved in an abstract setting distribution vanishes on the complement of the set. ## Main statements -* `TemperedDistribution.dsupport_delta`: The support of the delta distribution is a single point. +* `dsupport_delta`: The support of the delta distribution is a single point. Available for tempered + and classical distributions. -/ @@ -169,6 +171,8 @@ end normed open SchwartzMap Distribution TemperedDistribution +namespace TemperedDistribution + variable [NormedAddCommGroup E] [NormedAddCommGroup F] [NormedSpace ℝ E] [NormedSpace ℂ F] variable {f : 𝓢'(E, F)} {s : Set E} @@ -185,13 +189,16 @@ theorem smulLeftCLM (hf : IsVanishingOn f s) {g : E → ℂ} (hg : g.HasTemperat rw [SchwartzMap.smulLeftCLM_apply hg] exact (tsupport_smul_subset_right g u).trans hu +@[deprecated (since := "2026-07-01")] alias _root_.Distribution.IsVanishingOn.smulLeftCLM := + Distribution.TemperedDistribution.IsVanishingOn.smulLeftCLM + open LineDeriv @[fun_prop] theorem lineDerivOp (hf : IsVanishingOn f s) (m : E) : IsVanishingOn (∂_{m} f : 𝓢'(E, F)) s := by intro u hu - simp only [lineDerivOp_apply_apply, map_neg, neg_eq_zero] + simp only [TemperedDistribution.lineDerivOp_apply_apply, map_neg, neg_eq_zero] exact hf (∂_{m} u) <| (tsupport_fderiv_apply_subset ℝ m).trans hu @[fun_prop] @@ -201,7 +208,7 @@ theorem iteratedLineDerivOp {n : ℕ} (hf : IsVanishingOn f s) (m : Fin n → E) | zero => exact hf | succ n IH => - exact (IH <| Fin.tail m).lineDerivOp (m 0) + exact lineDerivOp (IH <| Fin.tail m) (m 0) @[fun_prop] theorem _root_.TemperedDistribution.isVanishingOn_delta (x : E) : @@ -218,6 +225,9 @@ theorem dsupport_smulLeftCLM_subset {g : E → ℂ} (hg : g.HasTemperateGrowth) dsupport (smulLeftCLM F g f) ⊆ dsupport f := by gcongr; fun_prop +@[deprecated (since := "2026-07-01")] alias _root_.Distribution.dsupport_smulLeftCLM_subset := + Distribution.TemperedDistribution.dsupport_smulLeftCLM_subset + open LineDeriv theorem dsupport_lineDerivOp_subset (m : E) : dsupport (∂_{m} f : 𝓢'(E, F)) ⊆ dsupport f := by @@ -245,4 +255,78 @@ theorem dsupport_delta [FiniteDimensional ℝ E] (x : E) : end Support +end TemperedDistribution + +/-! ## Classical distributions -/ + +open TopologicalSpace Distributions + +variable + {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] {Ω : Opens E} + {F : Type*} [AddCommGroup F] [Module ℝ F] [TopologicalSpace F] + [IsTopologicalAddGroup F] [ContinuousSMul ℝ F] + {n : ℕ∞} + +variable {f : 𝓓'(Ω, F)} {s : Set E} + +namespace IsVanishingOn + +open scoped Topology + +open LineDeriv + +@[fun_prop] +theorem lineDerivOp (hf : IsVanishingOn f s) (m : E) : + IsVanishingOn (∂_{m} f : 𝓓'(Ω, F)) s := by + intro u hu + simp only [Distribution.lineDerivOp_apply_apply, map_neg, neg_eq_zero] + exact hf (∂_{m} u) <| (tsupport_fderiv_apply_subset ℝ m).trans hu + +@[fun_prop] +theorem iteratedLineDerivOp {n : ℕ} (hf : IsVanishingOn f s) (m : Fin n → E) : + IsVanishingOn (∂^{m} f : 𝓓'(Ω, F)) s := by + induction n with + | zero => + exact hf + | succ n IH => + exact lineDerivOp (IH <| Fin.tail m) (m 0) + +@[fun_prop] +theorem _root_.Distribution.isVanishingOn_delta (x : E) : + IsVanishingOn (Distribution.delta x : 𝓓'^{n}(Ω, ℝ)) {x}ᶜ := by + intro u hu + rw [Set.subset_compl_singleton_iff] at hu + apply image_eq_zero_of_notMem_tsupport hu + +end IsVanishingOn + +section Support + +open LineDeriv + +theorem dsupport_lineDerivOp_subset (m : E) : dsupport (∂_{m} f : 𝓓'(Ω, F)) ⊆ dsupport f := by + gcongr; fun_prop + +theorem dsupport_iteratedLineDerivOp_subset {n : ℕ} (m : Fin n → E) : + dsupport (∂^{m} f : 𝓓'(Ω, F)) ⊆ dsupport f := by + gcongr; fun_prop + +theorem dsupport_delta [FiniteDimensional ℝ E] (x : E) (hx : x ∈ Ω) : + dsupport (Distribution.delta x : 𝓓'^{n}(Ω, ℝ)) = {x} := by + apply subset_antisymm + · intro x' hx' + rw [mem_dsupport_iff] at hx' + exact hx' {x} (isVanishingOn_delta x) (T1Space.t1 x) + rintro x rfl + rw [mem_dsupport_iff_forall_exists_ne] + intro s hxs hs + set t := s ∩ Ω + have ht : IsOpen t := hs.inter Ω.isOpen + have htx : x ∈ t := Set.mem_inter hxs hx + obtain ⟨u, h₁, h₂, h₃, -, h₄⟩ := + exists_contDiff_tsupport_subset (n := n) ((IsOpen.mem_nhds_iff ht).mpr htx) + exact ⟨⟨u, h₃, h₂, by aesop⟩, ⟨by aesop, by simp [h₄]⟩⟩ + +end Support + end Distribution diff --git a/Mathlib/Analysis/Distribution/TemperedDistribution.lean b/Mathlib/Analysis/Distribution/TemperedDistribution.lean index 5be14c0e45ed36..871955ee5ae633 100644 --- a/Mathlib/Analysis/Distribution/TemperedDistribution.lean +++ b/Mathlib/Analysis/Distribution/TemperedDistribution.lean @@ -175,13 +175,14 @@ theorem toTemperedDistribution_apply {p : ℝ≥0∞} [hp : Fact (1 ≤ p)] (f : filter_upwards [g.coeFn_toLp (1 - p⁻¹)⁻¹ μ] with x hg rw [hg] -instance instCoeDep {p : ℝ≥0∞} [hp : Fact (1 ≤ p)] (f : Lp F p μ) : - CoeDep (Lp F p μ) f 𝓢'(E, F) where - coe := toTemperedDistribution f +/-- This coercion has to be a `CoeHead`, because `𝓢'(E, F)` can't infer the value of `p` or `μ`. -/ +instance instCoeToTemperedDistribution {p : ℝ≥0∞} [hp : Fact (1 ≤ p)] : + CoeHead (Lp F p μ) 𝓢'(E, F) where + coe := toTemperedDistribution @[simp] theorem toTemperedDistribution_toLp_eq [SecondCountableTopology E] {p : ℝ≥0∞} [hp : Fact (1 ≤ p)] - (f : 𝓢(E, F)) : ((f.toLp p μ) : 𝓢'(E, F)) = f.toTemperedDistributionCLM E F μ := by + (f : 𝓢(E, F)) : ((f : Lp F p μ) : 𝓢'(E, F)) = f.toTemperedDistributionCLM E F μ := by ext g simp only [Lp.toTemperedDistribution_apply, toTemperedDistributionCLM_apply_apply] apply integral_congr_ae diff --git a/Mathlib/Analysis/Fourier/Convolution.lean b/Mathlib/Analysis/Fourier/Convolution.lean index b072ac2c74f100..72357bbda4f4af 100644 --- a/Mathlib/Analysis/Fourier/Convolution.lean +++ b/Mathlib/Analysis/Fourier/Convolution.lean @@ -41,26 +41,11 @@ variable [NontriviallyNormedField 𝕜] [NormedAddCommGroup E] [InnerProductSpace ℝ E] [FiniteDimensional ℝ E] [MeasurableSpace E] [BorelSpace E] [NormedSpace 𝕜 F₁] [NormedSpace 𝕜 F₂] [NormedSpace 𝕜 F₃] -/-- The norm of the integrant of the convolution is integrable if the functions are integrable -and continuous. -/ +/-- The norm of the integrand of the convolution is integrable if the functions are integrable. -/ theorem integrable_prod_sub (B : F₁ →L[𝕜] F₂ →L[𝕜] F₃) {f₁ : E → F₁} {f₂ : E → F₂} - (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂) : + (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) : Integrable (fun (p : E × E) ↦ ‖B‖ * (‖f₁ (p.1 - p.2)‖ * ‖f₂ p.2‖)) (volume.prod volume) := by - apply Integrable.const_mul - rw [integrable_prod_iff' (by fun_prop)] - constructor - · filter_upwards with x - exact (hf₁.comp_sub_right x).norm.mul_const _ - have : Integrable (fun x ↦ ((∫ y, ‖f₁ y‖) * ‖f₂ x‖)) := by - apply hf₂.norm.bdd_mul (by fun_prop) (c := ‖(∫ y, ‖f₁ y‖)‖) - filter_upwards with; rfl - convert! this using 1 - ext x - simp_rw [norm_mul, norm_norm] - rw [integral_mul_const] - congr 1 - convert! integral_sub_right_eq_self _ x (μ := volume) - rfl + simpa [mul_comm] using (hf₂.norm.convolution_integrand (.mul ℝ ℝ) hf₁.norm).const_mul ‖B‖ open FourierTransform @@ -68,8 +53,7 @@ variable [NormedSpace ℂ F₃] /-- Calculate the Fourier transform of the convolution as a symmetric integral. -/ theorem fourier_bilin_convolution_eq_integral (B : F₁ →L[𝕜] F₂ →L[𝕜] F₃) {f₁ : E → F₁} {f₂ : E → F₂} - (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂) - (ξ : E) : + (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) : 𝓕 (f₁ ⋆[B] f₂) ξ = ∫ y, ∫ x, 𝐞 (-inner ℝ (y + x) ξ) • B (f₁ x) (f₂ y) := calc _ = 𝓕 (f₂ ⋆[B.flip] f₁) ξ := by rw [convolution_flip] @@ -80,11 +64,11 @@ theorem fourier_bilin_convolution_eq_integral (B : F₁ →L[𝕜] F₂ →L[ simp_rw [Circle.smul_def, integral_smul] _ = ∫ y, ∫ x, 𝐞 (-inner ℝ x ξ) • B (f₁ (x - y)) (f₂ y) := by refine integral_integral_swap ?_ - apply (integrable_prod_sub B hf₁ hf₂ hf₁' hf₂').mono (by measurability) - filter_upwards with ⟨y, x⟩ - have : ‖(B (f₁ (y - x))) (f₂ x)‖ ≤ ‖B‖ * (‖f₁ (y - x)‖ * ‖f₂ x‖) := by - grw [B.le_opNorm₂ (f₁ (y - x)) (f₂ x), mul_assoc] - simpa + have hB := hf₂.convolution_integrand B.flip hf₁ + refine hB.mono ?_ ?_ + · exact continuous_fourierChar.comp (by fun_prop) |>.aestronglyMeasurable.smul + hB.aestronglyMeasurable + · filter_upwards with ⟨x, y⟩ using by simp _ = ∫ y, ∫ x, 𝐞 (-inner ℝ (y + x) ξ) • B (f₁ x) (f₂ y) := by congr ext y @@ -101,31 +85,18 @@ open ContinuousLinearMap /-- The Fourier transform of the convolution is given by the bilinear map applied to the Fourier transform of the individual functions. -/ theorem fourier_bilin_convolution_eq (B : F₁ →L[ℂ] F₂ →L[ℂ] F₃) {f₁ : E → F₁} {f₂ : E → F₂} - (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂) - (ξ : E) : + (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) : 𝓕 (f₁ ⋆[B] f₂) ξ = B (𝓕 f₁ ξ) (𝓕 f₂ ξ) := calc _ = ∫ y, ∫ x, 𝐞 (-inner ℝ (y + x) ξ) • B (f₁ x) (f₂ y) := - fourier_bilin_convolution_eq_integral B hf₁ hf₂ hf₁' hf₂' _ + fourier_bilin_convolution_eq_integral B hf₁ hf₂ _ _ = ∫ y, ∫ x, 𝐞 (-inner ℝ y ξ) • 𝐞 (-inner ℝ x ξ) • B (f₁ x) (f₂ y) := by - congr - ext y - congr - ext x - rw [smul_smul, ← AddChar.map_add_eq_mul, inner_add_left] - congr - grind + simp_rw [inner_add_left, neg_add, AddChar.map_add_eq_mul, smul_smul] _ = ∫ y, (∫ x, B (𝐞 (-inner ℝ x ξ) • f₁ x)) (𝐞 (-inner ℝ y ξ) • f₂ y) := by - congr - ext y - simp_rw [Circle.smul_def, map_smul, MeasureTheory.integral_smul] - congr - rw [integral_apply ?_ (f₂ y)] - · simp - have : MeasureTheory.Integrable (fun x ↦ ‖B‖ * ‖f₁ x‖) MeasureTheory.volume := - hf₁.norm.const_mul _ - apply this.mono (by fun_prop) - filter_upwards with x - simpa [← Circle.smul_def] using le_opNorm B (f₁ x) + congr with y + have : Integrable (fun x ↦ (𝐞 (-inner ℝ x ξ) : ℂ) • B (f₁ x)) volume := by + simpa [Circle.smul_def] using + (Real.fourierIntegral_convergent_iff ξ).2 (B.integrable_comp hf₁) + simp [Circle.smul_def, MeasureTheory.integral_smul, integral_apply this (f₂ y)] _ = B (∫ x, 𝐞 (-inner ℝ x ξ) • f₁ x) (∫ y, 𝐞 (-inner ℝ y ξ) • f₂ y) := by rw [← integral_comp_comm _ (by simpa using hf₂), ← integral_comp_comm _ (by simpa using hf₁)] @@ -134,10 +105,9 @@ of the individual functions. Version for scalar multiplication. -/ theorem fourier_smul_convolution_eq {f₁ : E → ℂ} {f₂ : E → F₁} - (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂) - (ξ : E) : + (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) : 𝓕 (f₁ ⋆[lsmul ℂ ℂ] f₂) ξ = (𝓕 f₁ ξ) • (𝓕 f₂ ξ) := - fourier_bilin_convolution_eq (lsmul ℂ ℂ) hf₁ hf₂ hf₁' hf₂' ξ + fourier_bilin_convolution_eq (lsmul ℂ ℂ) hf₁ hf₂ ξ variable [NormedRing R] [NormedSpace ℂ R] [IsScalarTower ℂ R R] [SMulCommClass ℂ R R] [CompleteSpace R] @@ -147,10 +117,9 @@ of the individual functions. Version for multiplication. -/ theorem fourier_mul_convolution_eq {f₁ : E → R} {f₂ : E → R} - (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (hf₁' : Continuous f₁) (hf₂' : Continuous f₂) - (ξ : E) : + (hf₁ : Integrable f₁) (hf₂ : Integrable f₂) (ξ : E) : 𝓕 (f₁ ⋆[mul ℂ R] f₂) ξ = (𝓕 f₁ ξ) * (𝓕 f₂ ξ) := - fourier_bilin_convolution_eq (mul ℂ R) hf₁ hf₂ hf₁' hf₂' ξ + fourier_bilin_convolution_eq (mul ℂ R) hf₁ hf₂ ξ end Real @@ -198,7 +167,7 @@ open MeasureTheory theorem fourier_convolution_apply (B : F₁ →L[ℂ] F₂ →L[ℂ] F₃) (f : 𝓢(E, F₁)) (g : 𝓢(E, F₂)) (x : E) : 𝓕 (convolution B f g) x = 𝓕 (f ⋆[B] g) x := by simp [fourier_convolution, fourier_coe, - Real.fourier_bilin_convolution_eq B f.integrable g.integrable f.continuous g.continuous] + Real.fourier_bilin_convolution_eq B f.integrable g.integrable] /-- The convolution on Schwartz functions is equal to the convolution on functions. -/ theorem convolution_apply (B : F₁ →L[ℂ] F₂ →L[ℂ] F₃) (f : 𝓢(E, F₁)) (g : 𝓢(E, F₂)) (x : E) : diff --git a/Mathlib/Analysis/Fourier/FourierTransform.lean b/Mathlib/Analysis/Fourier/FourierTransform.lean index 7023f5af93e170..7d12ab08d9f1e3 100644 --- a/Mathlib/Analysis/Fourier/FourierTransform.lean +++ b/Mathlib/Analysis/Fourier/FourierTransform.lean @@ -140,7 +140,7 @@ theorem fourierIntegral_convergent_iff (he : Continuous e) have aux {g : V → E} (hg : Integrable g μ) (x : W) : Integrable (fun v : V ↦ e (-L v x) • g v) μ := by have c : Continuous fun v ↦ e (-L v x) := he.comp (hL.comp (.prodMk_left _)).neg - simp_rw [← integrable_norm_iff (c.aestronglyMeasurable.smul hg.1), Circle.norm_smul] + simp_rw [← integrable_norm_iff (c.aestronglyMeasurable.fun_smul hg.1), Circle.norm_smul] exact hg.norm -- then use it for both directions refine ⟨fun hf ↦ ?_, fun hf ↦ aux hf w⟩ @@ -177,7 +177,7 @@ section Fubini variable [TopologicalSpace 𝕜] [IsTopologicalRing 𝕜] [TopologicalSpace V] [BorelSpace V] [TopologicalSpace W] [MeasurableSpace W] [BorelSpace W] {e : AddChar 𝕜 𝕊} {μ : Measure V} {L : V →ₗ[𝕜] W →ₗ[𝕜] 𝕜} - {ν : Measure W} [SigmaFinite μ] [SigmaFinite ν] [SecondCountableTopology V] + {ν : Measure W} [SigmaFinite μ] [SigmaFinite ν] [SecondCountableTopologyEither W V] variable {σ : ℂ →+* ℂ} [RingHomIsometric σ] @@ -196,7 +196,7 @@ theorem integral_fourierIntegral_swap apply this.mono · change AEStronglyMeasurable (fun p : W × V ↦ (M (g p.1) (e (-(L p.2) p.1) • f p.2))) _ have A : AEStronglyMeasurable (fun (p : W × V) ↦ e (-L p.2 p.1) • f p.2) (ν.prod μ) := by - refine (Continuous.aestronglyMeasurable ?_).smul hf.1.comp_snd + refine (Continuous.aestronglyMeasurable ?_).fun_smul hf.1.comp_snd exact he.comp (hL.comp continuous_swap).neg have A' : AEStronglyMeasurable (fun p ↦ (g p.1, e (-(L p.2) p.1) • f p.2) : W × V → F × E) (Measure.prod ν μ) := hg.1.comp_fst.prodMk A diff --git a/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean b/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean index 69ce46ebc9901f..15d25a14e6e7a9 100644 --- a/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean +++ b/Mathlib/Analysis/Fourier/FourierTransformDeriv.lean @@ -197,7 +197,7 @@ lemma _root_.MeasureTheory.AEStronglyMeasurable.fourierSMulRight {L : V →L[ℝ] W →L[ℝ] ℝ} {f : V → E} {μ : Measure V} (hf : AEStronglyMeasurable f μ) : AEStronglyMeasurable (fun v ↦ fourierSMulRight L f v) μ := by - apply AEStronglyMeasurable.const_smul' + apply AEStronglyMeasurable.fun_const_smul have aux0 : Continuous fun p : (W →L[ℝ] ℝ) × E ↦ p.1.smulRight p.2 := (ContinuousLinearMap.smulRightL ℝ W E).continuous₂ have aux1 : AEStronglyMeasurable (fun v ↦ (L v, f v)) μ := @@ -224,7 +224,7 @@ theorem hasFDerivAt_fourierIntegral have h1 : ∀ᶠ w' in 𝓝 w, AEStronglyMeasurable (F w') μ := Eventually.of_forall (fun w' ↦ (h0 w').aestronglyMeasurable) have h3 : AEStronglyMeasurable (F' w) μ := by - refine .smul ?_ hf.1.fourierSMulRight + refine .fun_smul ?_ hf.1.fourierSMulRight refine (continuous_fourierChar.comp ?_).aestronglyMeasurable fun_prop have h4 : (∀ᵐ v ∂μ, ∀ (w' : W), w' ∈ Metric.ball w 1 → ‖F' w' v‖ ≤ B v) := by @@ -310,7 +310,7 @@ lemma fourierPowSMulRight_eq_comp {f : V → E} {v : V} {n : ℕ} : lemma _root_.Continuous.fourierPowSMulRight {f : V → E} (hf : Continuous f) (n : ℕ) : Continuous (fun v ↦ fourierPowSMulRight L f v n) := by simp_rw [fourierPowSMulRight_eq_comp] - apply Continuous.const_smul + apply Continuous.fun_const_smul apply (smulRightL ℝ (fun (_ : Fin n) ↦ W) E).continuous₂.comp₂ _ hf exact Continuous.comp (map_continuous _) (continuous_pi (fun _ ↦ L.continuous)) @@ -433,7 +433,7 @@ lemma _root_.MeasureTheory.AEStronglyMeasurable.fourierPowSMulRight (hf : AEStronglyMeasurable f μ) (n : ℕ) : AEStronglyMeasurable (fun v ↦ fourierPowSMulRight L f v n) μ := by simp_rw [fourierPowSMulRight_eq_comp] - apply AEStronglyMeasurable.const_smul' + apply AEStronglyMeasurable.fun_const_smul apply (smulRightL ℝ (fun (_ : Fin n) ↦ W) E).continuous₂.comp_aestronglyMeasurable₂ _ hf apply Continuous.aestronglyMeasurable exact Continuous.comp (map_continuous _) (continuous_pi (fun _ ↦ L.continuous)) @@ -487,8 +487,7 @@ lemma hasFTaylorSeriesUpTo_fourierIntegral {N : ℕ∞ω} congr with v simp only [fourierPowSMulRight_apply, mul_comm, pow_succ, neg_mul, Fin.prod_univ_succ, Fin.cons_zero, Fin.cons_succ, neg_smul, fourierSMulRight_apply, - ContinuousMultilinearMap.neg_apply, ContinuousMultilinearMap.smul_apply, - smul_comm (M := ℝ) (N := ℂ) (α := E), smul_smul] + neg_apply, smul_apply, smul_comm (M := ℝ) (N := ℂ) (α := E), smul_smul] exact E ▸ hasFDerivAt_fourierIntegral L I₁ I₂ w · intro n hn apply fourierIntegral_continuous Real.continuous_fourierChar (by apply L.continuous₂) @@ -538,9 +537,9 @@ theorem fourierIntegral_iteratedFDeriv [FiniteDimensional ℝ V] induction n with | zero => ext w m - simp only [iteratedFDeriv_zero_apply, fourierPowSMulRight_apply, pow_zero, - Finset.univ_eq_empty, _root_.neg_apply, ContinuousLinearMap.flip_apply, - Finset.prod_empty, one_smul, fourierIntegral_continuousMultilinearMap_apply' ((h'f 0 bot_le))] + simp only [iteratedFDeriv_zero_apply, fourierPowSMulRight_apply, pow_zero, Finset.univ_eq_empty, + neg_apply, ContinuousLinearMap.flip_apply, Finset.prod_empty, one_smul, + fourierIntegral_continuousMultilinearMap_apply' ((h'f 0 bot_le))] | succ n ih => ext w m have J : Integrable (fderiv ℝ (iteratedFDeriv ℝ n f)) μ := by @@ -552,18 +551,17 @@ theorem fourierIntegral_iteratedFDeriv [FiniteDimensional ℝ V] (m 0) (Fin.tail m) = (-(2 * π * I)) ^ (n + 1) • (∏ x : Fin (n + 1), -L (m x) w) • ∫ v, 𝐞 (-L v w) • f v ∂μ by rw [fourierIntegral_continuousMultilinearMap_apply' (h'f _ hn)] - simp only [iteratedFDeriv_succ_apply_left, fourierPowSMulRight_apply, - _root_.neg_apply, ContinuousLinearMap.flip_apply] + simp only [iteratedFDeriv_succ_apply_left, fourierPowSMulRight_apply, neg_apply, + ContinuousLinearMap.flip_apply] rw [← fourierIntegral_continuousMultilinearMap_apply' ((J.apply_continuousLinearMap _)), ← fourierIntegral_continuousLinearMap_apply' J] exact H have h'n : n < N := (Nat.cast_lt.mpr n.lt_succ_self).trans_le hn rw [fourierIntegral_fderiv _ (h'f n h'n.le) (hf.differentiable_iteratedFDeriv (mod_cast h'n)) J] - simp only [ih h'n.le, fourierSMulRight_apply, _root_.neg_apply, - ContinuousLinearMap.flip_apply, neg_smul, smul_neg, neg_neg, - ContinuousMultilinearMap.smul_apply, fourierPowSMulRight_apply, - ← coe_smul (E := E), smul_smul] + simp only [ih h'n.le, fourierSMulRight_apply, neg_apply, ContinuousLinearMap.flip_apply, + neg_smul, smul_neg, neg_neg, smul_apply, fourierPowSMulRight_apply, ← coe_smul (E := E), + smul_smul] congr 1 simp only [ofReal_prod, ofReal_neg, pow_succ, mul_neg, Fin.prod_univ_succ, neg_mul, ofReal_mul, neg_neg, Fin.tail_def] diff --git a/Mathlib/Analysis/InnerProductSpace/Defs.lean b/Mathlib/Analysis/InnerProductSpace/Defs.lean index cc34ea1e8dda94..2bc916e9ff9f00 100644 --- a/Mathlib/Analysis/InnerProductSpace/Defs.lean +++ b/Mathlib/Analysis/InnerProductSpace/Defs.lean @@ -103,6 +103,7 @@ Note that `NormedSpace` does not assume that `‖x‖=0` implies `x=0` (it is ra To construct a seminorm from an inner product, see `PreInnerProductSpace.ofCore`. -/ +@[wikidata Q214159] class InnerProductSpace (𝕜 : Type*) (E : Type*) [RCLike 𝕜] [SeminormedAddCommGroup E] extends NormedSpace 𝕜 E, Inner 𝕜 E where /-- The inner product induces the norm. -/ diff --git a/Mathlib/Analysis/InnerProductSpace/JointEigenspace.lean b/Mathlib/Analysis/InnerProductSpace/JointEigenspace.lean index a2b446dbaae0ef..97df41135bc48e 100644 --- a/Mathlib/Analysis/InnerProductSpace/JointEigenspace.lean +++ b/Mathlib/Analysis/InnerProductSpace/JointEigenspace.lean @@ -140,6 +140,7 @@ theorem directSum_isInternal_of_pairwise_commute [DecidableEq (n → 𝕜)] · rw [iSup_iInf_eq_top_of_commute hT hC, top_orthogonal_eq_bot] · exact orthogonalFamily_iInf_eigenspaces hT +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias LinearMap.IsSymmetric.directSum_isInternal_of_pairwise_commute := directSum_isInternal_of_pairwise_commute diff --git a/Mathlib/Analysis/InnerProductSpace/Laplacian.lean b/Mathlib/Analysis/InnerProductSpace/Laplacian.lean index b75c2c3ef2661b..2041f4bc929c6d 100644 --- a/Mathlib/Analysis/InnerProductSpace/Laplacian.lean +++ b/Mathlib/Analysis/InnerProductSpace/Laplacian.lean @@ -128,6 +128,7 @@ variable (f s) in Laplacian for functions on real inner product spaces, with respect to a set `s`. Use `open InnerProductSpace` to access the notation `Δ[s]` for `InnerProductSpace.LaplacianWithin`. -/ +@[wikidata Q203484] noncomputable def laplacianWithin : E → F := fun x ↦ tensorIteratedFDerivWithinTwo ℝ f s x (InnerProductSpace.canonicalCovariantTensor E) diff --git a/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean b/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean index 82411d48abf41a..7a191b6d044670 100644 --- a/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean +++ b/Mathlib/Analysis/InnerProductSpace/Orthogonal.lean @@ -6,7 +6,7 @@ Authors: Zhouhang Zhou, Sébastien Gouëzel, Frédéric Dupuis module public import Mathlib.Analysis.InnerProductSpace.Subspace -public import Mathlib.LinearAlgebra.SesquilinearForm.Basic +public import Mathlib.LinearAlgebra.SesquilinearForm.Orthogonal public import Mathlib.Topology.Algebra.Module.ClosedSubmodule /-! diff --git a/Mathlib/Analysis/InnerProductSpace/PiL2.lean b/Mathlib/Analysis/InnerProductSpace/PiL2.lean index 9836ea7f5a2ede..297d9435e95b06 100644 --- a/Mathlib/Analysis/InnerProductSpace/PiL2.lean +++ b/Mathlib/Analysis/InnerProductSpace/PiL2.lean @@ -10,6 +10,7 @@ public import Mathlib.Analysis.Normed.Lp.PiLp public import Mathlib.Analysis.Normed.Lp.Matrix public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas public import Mathlib.LinearAlgebra.UnitaryGroup +public import Mathlib.Tactic.CrossRefAttribute public import Mathlib.Util.Superscript /-! @@ -108,6 +109,7 @@ space use `EuclideanSpace 𝕜 (Fin n)`. For the case when `n = Fin _`, there is `!₂[x, y, ...]` notation for building elements of this type, analogous to `![x, y, ...]` notation. -/ +@[wikidata Q17295] abbrev EuclideanSpace (𝕜 : Type*) (n : Type*) : Type _ := PiLp 2 fun _ : n => 𝕜 @@ -154,6 +156,7 @@ theorem EuclideanSpace.real_norm_sq_eq {n : Type*} [Fintype n] (x : EuclideanSpa ‖x‖ ^ 2 = ∑ i, (x i) ^ 2 := by simp [EuclideanSpace.norm_sq_eq] +@[wikidata Q847073] theorem EuclideanSpace.dist_eq {𝕜 : Type*} [RCLike 𝕜] {n : Type*} [Fintype n] (x y : EuclideanSpace 𝕜 n) : dist x y = √(∑ i, dist (x i) (y i) ^ 2) := PiLp.dist_eq_of_L2 x y @@ -1319,7 +1322,7 @@ open Matrix LinearMap EuclideanSpace in theorem InnerProductSpace.symm_toEuclideanLin_rankOne {𝕜 m n : Type*} [RCLike 𝕜] [Fintype m] [Fintype n] [DecidableEq n] (x : EuclideanSpace 𝕜 m) (y : EuclideanSpace 𝕜 n) : toEuclideanLin.symm (rankOne 𝕜 x y) = .vecMulVec x (star y) := by - simp [toLpLin, toMatrix', ← ext_iff, vecMulVec_apply, inner_single_right, mul_comm] + simp [toLpLin, toMatrix', ← Matrix.ext_iff, vecMulVec_apply, inner_single_right, mul_comm] namespace FiniteDimensional variable [Unique ι] (h : Module.finrank 𝕜 E = 1) {v : E} (hv : ‖v‖ = 1) @@ -1332,7 +1335,7 @@ def orthonormalBasisSingleton : OrthonormalBasis ι 𝕜 E := @[simp] theorem orthonormalBasisSingleton_apply (i : ι) : - orthonormalBasisSingleton ι 𝕜 h v hv i = v := by + orthonormalBasisSingleton ι 𝕜 h v hv i = v := by simp [orthonormalBasisSingleton] @[simp] diff --git a/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean b/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean index 95e52bd3f93f08..09797f0b3d9bc8 100644 --- a/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean +++ b/Mathlib/Analysis/InnerProductSpace/Projection/Basic.lean @@ -434,7 +434,8 @@ theorem orthogonalProjectionOnto_apply_of_mem_orthogonal [K.HasOrthogonalProjection] {v : E} (hv : v ∈ Kᗮ) : K.orthogonalProjectionOnto v = 0 := orthogonalProjectionOnto_eq_zero_iff.mpr hv -@[deprecated (since := "2026-05-06")] alias orthogonalProjection_apply_of_mem_orthogonal := +@[deprecated (since := "2026-05-06")] alias +orthogonalProjection_mem_subspace_orthogonalComplement_eq_zero := orthogonalProjectionOnto_apply_of_mem_orthogonal /-- The projection into `U` from an orthogonal submodule `V` is the zero map. -/ diff --git a/Mathlib/Analysis/InnerProductSpace/Reproducing.lean b/Mathlib/Analysis/InnerProductSpace/Reproducing.lean index 7d3d0ed6787d99..afe6444ebe321c 100644 --- a/Mathlib/Analysis/InnerProductSpace/Reproducing.lean +++ b/Mathlib/Analysis/InnerProductSpace/Reproducing.lean @@ -108,6 +108,12 @@ lemma kerFun_apply (y : X) (v : V) (x : X) : kerFun H y v x = kernel H x y v := lemma kernel_apply (x y : X) : kernel H x y = (kerFun H x).adjoint ∘L kerFun H y := by simp [kerFun, kernel] +variable {H} in +/-- Point evaluation `f ↦ f x` is the adjoint of the kernel function `kerFun H x`. -/ +@[simp] +lemma adjoint_kerFun (x : X) (f : H) : (kerFun H x).adjoint f = f x := by + simp [kerFun] + variable {H} in /-- The "reproducing" property of the kernel functions, left version. -/ @[simp] @@ -138,6 +144,12 @@ lemma norm_kernel_le (x y) : ‖kernel H x y‖ ≤ √‖kernel H x x‖ * √ lemma norm_kernel_sq_le (x y) : ‖kernel H x y‖ ^ 2 ≤ ‖kernel H x x‖ * ‖kernel H y y‖ := by grw [norm_kernel_le]; simp [mul_pow] +variable {H} in +/-- The evaluation of an element `f` of a reproducing kernel Hilbert space at a point `x` is +bounded by `‖f‖` times the square root of the kernel diagonal `‖kernel H x x‖` at `x`. -/ +lemma norm_apply_le (f : H) (x : X) : ‖f x‖ ≤ ‖f‖ * √‖kernel H x x‖ := by + grw [← adjoint_kerFun, le_opNorm, norm_map, norm_kerFun_eq_sqrt_norm_kernel, mul_comm] + /-- The span of the kernel functions is dense. -/ theorem kerFun_dense : topologicalClosure (span 𝕜 {kerFun H x v | (x) (v)}) = ⊤ := by refine (orthogonal_eq_bot_iff.mp ((Submodule.eq_bot_iff _).mpr fun f fin ↦ DFunLike.ext f 0 ?_)) diff --git a/Mathlib/Analysis/InnerProductSpace/TensorProduct.lean b/Mathlib/Analysis/InnerProductSpace/TensorProduct.lean index 8a195258189685..e2360e5c1d9801 100644 --- a/Mathlib/Analysis/InnerProductSpace/TensorProduct.lean +++ b/Mathlib/Analysis/InnerProductSpace/TensorProduct.lean @@ -8,6 +8,8 @@ module public import Mathlib.Analysis.InnerProductSpace.Adjoint public import Mathlib.LinearAlgebra.TensorProduct.Finiteness public import Mathlib.RingTheory.TensorProduct.Finite +import Mathlib.Analysis.InnerProductSpace.GramMatrix +import Mathlib.Analysis.InnerProductSpace.Positive /-! @@ -32,13 +34,13 @@ inner product spaces. * `TensorProduct.commIsometry`: the linear isometry version of `TensorProduct.comm`. * `TensorProduct.lidIsometry`: the linear isometry version of `TensorProduct.lid`. * `TensorProduct.assocIsometry`: the linear isometry version of `TensorProduct.assoc`. +* `TensorProduct.mapL`: the continuous version of `TensorProduct.map f g` when + `f` and `g` are continuous linear maps. * `OrthonormalBasis.tensorProduct`: the orthonormal basis of the tensor product of two orthonormal bases. ## TODO: -* Define the continuous linear map version of `TensorProduct.map`. -* Complete space of tensor products. * Define the normed space without needing inner products, this should be analogous to `Mathlib/Analysis/NormedSpace/PiTensorProduct/InjectiveSeminorm.lean`. @@ -409,21 +411,261 @@ noncomputable def assocIsometry : E ⊗[𝕜] F ⊗[𝕜] G ≃ₗᵢ[𝕜] E end isometry --- TODO: upgrade `map` to a `ContinuousLinearMap` -@[simp] theorem adjoint_map [FiniteDimensional 𝕜 E] [FiniteDimensional 𝕜 F] [FiniteDimensional 𝕜 G] - [FiniteDimensional 𝕜 H] (f : E →ₗ[𝕜] F) (g : G →ₗ[𝕜] H) : - LinearMap.adjoint (map f g) = map (LinearMap.adjoint f) (LinearMap.adjoint g) := - ext' fun _ _ => by simp [TensorProduct.ext_iff_inner_right, LinearMap.adjoint_inner_left] +end TensorProduct + +namespace ContinuousLinearMap + +open TensorProduct + +variable (G) + +/-- `LinearMap.rTensor` as a continuous linear map, i.e. the continuous linear map `f` extended to +the map `x ⊗ₜ[𝕜] y ↦ f(x) ⊗ₜ[𝕜] y`. -/ +noncomputable def rTensor (f : E →L[𝕜] F) : (E ⊗[𝕜] G) →L[𝕜] (F ⊗[𝕜] G) := + (f.toLinearMap.rTensor G).mkContinuous ‖f‖ fun x ↦ by + /- + Any tensor `x` can be written as a linear combination of pure tensors, `x = ∑ e n ⊗ₜ g n`. This + induces three Gram matrices, one based on `e`, one on `f ∘ e` and one on `g`. Up to a constant, + the `e`-based Gram matrix is larger than the `f ∘ e`-based one. This implies the existence of + a matrix, whose form is used to show that `‖f‖ ^ 2 * ‖x‖ ^ 2 - ‖f x‖ ^ 2` is a sum of + nonnegative terms. + -/ + obtain ⟨n, e, g, hx⟩ := exists_sum_tmul_eq x + obtain ⟨c, hc_supp, hc⟩ := Submodule.mem_span_set.mp + ((span_tmul_eq_top 𝕜 E G) ▸ Submodule.mem_top (x := x)) + obtain ⟨m, A, hA⟩ := Matrix.posSemidef_iff_eq_sum_vecMulVec.mp + (Matrix.posSemidef_opNorm_smul_gram_sub_gram e f) + apply (sq_le_sq₀ (norm_nonneg _) (by positivity)).mp + simp_rw [sub_eq_iff_eq_add', ← sub_eq_iff_eq_add, ← Matrix.ext_iff, Matrix.sub_apply, + Matrix.smul_apply, Matrix.gram_apply, Function.comp_apply] at hA + simp_rw [mul_pow, hx, map_sum, LinearMap.rTensor_tmul, coe_coe, + ← inner_self_eq_norm_sq (𝕜 := 𝕜), inner_sum, sum_inner, inner_tmul, ← hA, sub_mul, + Finset.sum_sub_distrib, map_sub, ← RCLike.smul_re, Finset.smul_sum, smul_mul_assoc, + sub_le_self_iff, Matrix.sum_apply, mul_comm, Finset.mul_sum] + simp_rw +singlePass [Finset.sum_comm_cycle, Matrix.vecMulVec, Matrix.of_apply, Pi.star_apply, + ← mul_left_comm, ← mul_assoc, ← starRingEnd_self_apply (A _ _), ← inner_smul_left] + simp [mul_comm, ← inner_smul_right, ← sum_inner, ← inner_sum, Finset.sum_nonneg] + +variable {G} in +@[simp] lemma rTensor_apply (f : E →L[𝕜] F) (x : E ⊗ G) : + f.rTensor G x = f.toLinearMap.rTensor G x := rfl + +variable {G} in +lemma rTensor_tmul (f : E →L[𝕜] F) (m : E) (n : G) : f.rTensor G (m ⊗ₜ n) = f m ⊗ₜ n := rfl + +@[simp] lemma toLinearMap_rTensor (f : E →L[𝕜] F) : + (f.rTensor G).toLinearMap = f.toLinearMap.rTensor G := rfl + +@[simp] lemma _root_.LinearIsometry.toContinuousLinearMap_rTensor (f : E →ₗᵢ[𝕜] F) : + (f.rTensor G).toContinuousLinearMap = f.toContinuousLinearMap.rTensor G := rfl + +theorem norm_rTensor_le (f : E →L[𝕜] F) : ‖f.rTensor G‖ ≤ ‖f‖ := + LinearMap.mkContinuous_norm_le _ (norm_nonneg _) _ + +@[simp] lemma rTensor_add (f₁ f₂ : E →L[𝕜] F) : + (f₁ + f₂).rTensor G = f₁.rTensor G + f₂.rTensor G := by ext; simp + +@[simp] lemma rTensor_smul (r : 𝕜) (f : E →L[𝕜] F) : + (r • f).rTensor G = r • f.rTensor G := by ext; simp + +@[simp] lemma rTensor_id : (.id 𝕜 E : E →L[𝕜] E).rTensor G = .id 𝕜 _ := by ext; simp +@[simp] lemma rTensor_one : (1 : E →L[𝕜] E).rTensor G = 1 := rTensor_id _ +@[simp] lemma rTensor_zero : (0 : E →L[𝕜] F).rTensor G = 0 := by ext; simp +@[simp] lemma rTensor_neg (f : E →L[𝕜] F) : (-f).rTensor G = -f.rTensor G := by ext; simp + +@[simp] lemma rTensor_sub (f₁ f₂ : E →L[𝕜] F) : + (f₁ - f₂).rTensor G = f₁.rTensor G - f₂.rTensor G := by ext; simp + +lemma rTensor_comp (f₁ : E →L[𝕜] F) (f₂ : H →L[𝕜] E) : + (f₁ ∘L f₂).rTensor G = f₁.rTensor G ∘L f₂.rTensor G := by ext; simp [LinearMap.rTensor_comp] + +lemma rTensor_mul (f₁ f₂ : E →L[𝕜] E) : (f₁ * f₂).rTensor G = f₁.rTensor G * f₂.rTensor G := + rTensor_comp _ _ _ + +@[simp] lemma rTensor_pow (f : E →L[𝕜] E) (n : ℕ) : (f ^ n).rTensor G = (f.rTensor G) ^ n := by + simp [← coe_inj] + +/-- `LinearMap.lTensor` as a continuous linear map, i.e. the continuous linear map `g` extended to +the map `x ⊗ₜ[𝕜] y ↦ x ⊗ₜ[𝕜] g(y)`. -/ +noncomputable def lTensor (g : E →L[𝕜] F) : (G ⊗[𝕜] E) →L[𝕜] (G ⊗[𝕜] F) := + commIsometry 𝕜 F G ∘L g.rTensor G ∘L commIsometry 𝕜 G E + +variable {G} in +@[simp] lemma lTensor_apply (g : G →L[𝕜] H) (x : E ⊗ G) : + g.lTensor E x = g.toLinearMap.lTensor E x := by + simp [lTensor, ← LinearMap.comm_comp_rTensor_comp_comm_eq] + +lemma lTensor_tmul (g : E →L[𝕜] F) (m : G) (n : E) : g.lTensor G (m ⊗ₜ n) = m ⊗ₜ g n := rfl + +theorem commIsometry_comp_lTensor_comp_commIsometry_eq (g : E →L[𝕜] F) : + commIsometry 𝕜 F G ∘L g.rTensor G ∘L commIsometry 𝕜 G E = g.lTensor G := + rfl + +theorem commIsometry_comp_rTensor_comp_commIsometry_eq (f : E →L[𝕜] F) : + commIsometry 𝕜 G F ∘L f.lTensor G ∘L commIsometry 𝕜 E G = f.rTensor G := by + ext; simp [lTensor] + +theorem lTensor_comp_commIsometry (f : E →L[𝕜] F) : + f.lTensor G ∘L commIsometry 𝕜 E G = commIsometry 𝕜 F G ∘L f.rTensor G := by + ext; simp [lTensor] + +theorem rTensor_comp_commIsometry (g : E →L[𝕜] F) : + g.rTensor G ∘L commIsometry 𝕜 G E = commIsometry 𝕜 G F ∘L g.lTensor G := by + ext; simp [lTensor] + +@[simp] lemma toLinearMap_lTensor (g : E →L[𝕜] F) : + (g.lTensor G).toLinearMap = g.toLinearMap.lTensor G := by ext; simp + +@[simp] lemma _root_.LinearIsometry.toContinuousLinearMap_lTensor (g : E →ₗᵢ[𝕜] F) : + (g.lTensor G).toContinuousLinearMap = g.toContinuousLinearMap.lTensor G := by ext; simp + +theorem norm_lTensor_le (g : E →L[𝕜] F) : ‖g.lTensor G‖ ≤ ‖g‖ := by + simp_rw [lTensor, ← LinearIsometryEquiv.toContinuousLinearMap_toLinearIsometry] + grw [opNorm_comp_le, opNorm_comp_le, LinearIsometry.norm_toContinuousLinearMap_le, + LinearIsometry.norm_toContinuousLinearMap_le, mul_one, one_mul, norm_rTensor_le] + +@[simp] lemma lTensor_add (f₁ f₂ : E →L[𝕜] F) : + (f₁ + f₂).lTensor G = f₁.lTensor G + f₂.lTensor G := by ext; simp + +@[simp] lemma lTensor_smul (r : 𝕜) (f : E →L[𝕜] F) : (r • f).lTensor G = r • f.lTensor G := by + ext; simp + +@[simp] lemma lTensor_id : (.id 𝕜 E : E →L[𝕜] E).lTensor G = .id 𝕜 _ := by ext; simp +@[simp] lemma lTensor_one : (1 : E →L[𝕜] E).lTensor G = 1 := lTensor_id _ +@[simp] lemma lTensor_zero : (0 : E →L[𝕜] F).lTensor G = 0 := by ext; simp +@[simp] lemma lTensor_neg (f : E →L[𝕜] F) : (-f).lTensor G = -f.lTensor G := by ext; simp + +@[simp] lemma lTensor_sub (f₁ f₂ : E →L[𝕜] F) : + (f₁ - f₂).lTensor G = f₁.lTensor G - f₂.lTensor G := by ext; simp + +lemma lTensor_comp (f₁ : E →L[𝕜] F) (f₂ : H →L[𝕜] E) : + (f₁ ∘L f₂).lTensor G = f₁.lTensor G ∘L f₂.lTensor G := by ext; simp [LinearMap.lTensor_comp] + +lemma lTensor_mul (f₁ f₂ : E →L[𝕜] E) : (f₁ * f₂).lTensor G = f₁.lTensor G * f₂.lTensor G := + lTensor_comp _ _ _ + +@[simp] lemma lTensor_pow (f : E →L[𝕜] E) (n : ℕ) : (f ^ n).lTensor G = (f.lTensor G) ^ n := by + simp [← coe_inj] + +end ContinuousLinearMap + +namespace TensorProduct + +/-- `TensorProduct.map` as a continuous linear map, i.e. the continuous linear map +`x ⊗ₜ[𝕜] y ↦ f(x) ⊗ₜ[𝕜] g(y)` formed from the continuous linear maps `f` and `g`. -/ +noncomputable def mapL (f : E →L[𝕜] F) (g : G →L[𝕜] H) : (E ⊗[𝕜] G) →L[𝕜] (F ⊗[𝕜] H) := + f.rTensor H ∘L g.lTensor E + +theorem norm_mapL_le (f : E →L[𝕜] F) (g : G →L[𝕜] H) : ‖mapL f g‖ ≤ ‖f‖ * ‖g‖ := by + grw [mapL, ContinuousLinearMap.opNorm_comp_le, ContinuousLinearMap.norm_rTensor_le, + ContinuousLinearMap.norm_lTensor_le] + +@[simp] lemma mapL_apply (f : E →L[𝕜] F) (g : G →L[𝕜] H) (x) : + mapL f g x = map f.toLinearMap g.toLinearMap x := by + simp [mapL, ← LinearMap.rTensor_comp_lTensor] + +lemma mapL_tmul (f : E →L[𝕜] F) (g : G →L[𝕜] H) (m : E) (n : G) : + mapL f g (m ⊗ₜ n) = f m ⊗ₜ g n := rfl + +@[simp] lemma mapL_zero_left (f : E →L[𝕜] F) : mapL (0 : G →L[𝕜] H) f = 0 := by simp [mapL] +@[simp] lemma mapL_zero_right (f : E →L[𝕜] F) : mapL f (0 : G →L[𝕜] H) = 0 := by simp [mapL] +@[simp] lemma mapL_id_id : mapL (.id 𝕜 E) (.id 𝕜 G) = .id 𝕜 _ := by simp [mapL] + +lemma mapL_comp_commIsometry (f : E →L[𝕜] F) (g : G →L[𝕜] H) : + mapL f g ∘L commIsometry 𝕜 G E = commIsometry 𝕜 H F ∘L mapL g f := by ext; simp [map_comm] + +lemma mapL_add_left (f₁ f₂ : E →L[𝕜] F) (g : G →L[𝕜] H) : + mapL (f₁ + f₂) g = mapL f₁ g + mapL f₂ g := by ext; simp [map_add_left] + +lemma mapL_add_right (f : E →L[𝕜] F) (g₁ g₂ : G →L[𝕜] H) : + mapL f (g₁ + g₂) = mapL f g₁ + mapL f g₂ := by ext; simp [map_add_right] + +lemma mapL_smul_left (r : 𝕜) (f : E →L[𝕜] F) (g : G →L[𝕜] H) : + mapL (r • f) g = r • mapL f g := by ext; simp [map_smul_left] + +lemma mapL_smul_right (r : 𝕜) (f : E →L[𝕜] F) (g : G →L[𝕜] H) : + mapL f (r • g) = r • mapL f g := by ext; simp [map_smul_right] + +@[simp] lemma toLinearMap_mapL (f : E →L[𝕜] F) (g : G →L[𝕜] H) : + (mapL f g).toLinearMap = map f g := by ext; simp + +@[simp] lemma toContinuousLinearMap_mapIsometry (f : E →ₗᵢ[𝕜] F) (g : G →ₗᵢ[𝕜] H) : + (mapIsometry f g).toContinuousLinearMap = + mapL f.toContinuousLinearMap g.toContinuousLinearMap := by + ext; simp + +section comp + +variable {A B : Type*} [NormedAddCommGroup A] [InnerProductSpace 𝕜 A] [NormedAddCommGroup B] + [InnerProductSpace 𝕜 B] + +lemma mapL_comp (f₁ : E →L[𝕜] F) (f₂ : A →L[𝕜] E) (g₁ : G →L[𝕜] H) (g₂ : B →L[𝕜] G) : + mapL (f₁ ∘L f₂) (g₁ ∘L g₂) = mapL f₁ g₁ ∘L mapL f₂ g₂ := by ext; simp [map_map] + +lemma mapL_mul (f₁ f₂ : E →L[𝕜] E) (g₁ g₂ : F →L[𝕜] F) : + mapL (f₁ * f₂) (g₁ * g₂) = mapL f₁ g₁ * mapL f₂ g₂ := mapL_comp _ _ _ _ + +@[simp] lemma mapL_pow (f : E →L[𝕜] E) (g : F →L[𝕜] F) (n : ℕ) : + (mapL f g) ^ n = mapL (f ^ n) (g ^ n) := by simp [← ContinuousLinearMap.coe_inj] + +@[simp] lemma _root_.ContinuousLinearMap.mapL_comp_rTensor (f₁ : E →L[𝕜] F) (f₂ : A →L[𝕜] E) + (g : G →L[𝕜] H) : mapL f₁ g ∘L f₂.rTensor G = mapL (f₁ ∘L f₂) g := by ext; simp + +@[simp] lemma _root_.ContinuousLinearMap.mapL_comp_lTensor (f : E →L[𝕜] F) (g₁ : G →L[𝕜] H) + (g₂ : A →L[𝕜] G) : mapL f g₁ ∘L g₂.lTensor E = mapL f (g₁ ∘L g₂) := by ext; simp + +@[simp] lemma _root_.ContinuousLinearMap.rTensor_comp_mapL (f₁ : E →L[𝕜] F) (f₂ : A →L[𝕜] E) + (g : G →L[𝕜] H) : f₁.rTensor H ∘L mapL f₂ g = mapL (f₁ ∘L f₂) g := by ext; simp + +@[simp] lemma _root_.ContinuousLinearMap.lTensor_comp_mapL (f : E →L[𝕜] F) (g₁ : G →L[𝕜] H) + (g₂ : A →L[𝕜] G) : g₁.lTensor F ∘L mapL f g₂ = mapL f (g₁ ∘L g₂) := by ext; simp + +end comp + +variable (G) in +theorem _root_.ContinuousLinearMap.rTensor_eq_mapL (f : E →L[𝕜] F) : + f.rTensor G = mapL f (.id 𝕜 G) := by simp [mapL] + +variable (E) in +theorem _root_.ContinuousLinearMap.lTensor_eq_mapL (g : G →L[𝕜] H) : + g.lTensor E = mapL (.id 𝕜 E) g := by simp [mapL] + +@[simp] lemma _root_.ContinuousLinearMap.lTensor_comp_rTensor (f : E →L[𝕜] F) (g : G →L[𝕜] H) : + f.lTensor H ∘L g.rTensor E = mapL g f := by ext; simp [← LinearMap.lTensor_comp_rTensor] + +@[simp] lemma _root_.ContinuousLinearMap.rTensor_comp_lTensor (f : E →L[𝕜] F) (g : G →L[𝕜] H) : + f.rTensor H ∘L g.lTensor E = mapL f g := rfl + +@[simp] theorem adjoint_mapL [CompleteSpace E] [CompleteSpace G] [CompleteSpace (E ⊗[𝕜] G)] + [CompleteSpace F] [CompleteSpace H] [CompleteSpace (F ⊗[𝕜] H)] + (f : E →L[𝕜] F) (g : G →L[𝕜] H) : (mapL f g).adjoint = mapL f.adjoint g.adjoint := by + apply ContinuousLinearMap.coe_inj.mp <| ext' ?_ + simp [TensorProduct.ext_iff_inner_right, ContinuousLinearMap.adjoint_inner_left] + +variable (G) in +@[simp] theorem _root_.ContinuousLinearMap.adjoint_rTensor [CompleteSpace E] [CompleteSpace G] + [CompleteSpace (E ⊗[𝕜] G)] [CompleteSpace (F ⊗[𝕜] G)] [CompleteSpace F] (f : E →L[𝕜] F) : + (f.rTensor G).adjoint = f.adjoint.rTensor G := by simp [ContinuousLinearMap.rTensor_eq_mapL] + +variable (E) in +@[simp] theorem _root_.ContinuousLinearMap.adjoint_lTensor [CompleteSpace E] [CompleteSpace G] + [CompleteSpace (E ⊗[𝕜] H)] [CompleteSpace (E ⊗[𝕜] G)] [CompleteSpace H] (g : G →L[𝕜] H) : + (g.lTensor E).adjoint = g.adjoint.lTensor E := by simp [ContinuousLinearMap.lTensor_eq_mapL] open LinearMap +@[simp] theorem adjoint_map [FiniteDimensional 𝕜 E] [FiniteDimensional 𝕜 F] [FiniteDimensional 𝕜 G] + [FiniteDimensional 𝕜 H] (f : E →ₗ[𝕜] F) (g : G →ₗ[𝕜] H) : + (map f g).adjoint = map f.adjoint g.adjoint := + ext' fun _ _ => by simp [TensorProduct.ext_iff_inner_right, adjoint_inner_left] + @[simp] theorem _root_.LinearMap.adjoint_rTensor [FiniteDimensional 𝕜 E] [FiniteDimensional 𝕜 F] [FiniteDimensional 𝕜 G] (f : E →ₗ[𝕜] F) : - adjoint (rTensor G f) = rTensor G f.adjoint := by simp [rTensor] + (f.rTensor G).adjoint = f.adjoint.rTensor G := by simp [rTensor] @[simp] theorem _root_.LinearMap.adjoint_lTensor [FiniteDimensional 𝕜 E] [FiniteDimensional 𝕜 F] [FiniteDimensional 𝕜 G] (f : E →ₗ[𝕜] F) : - adjoint (lTensor G f) = lTensor G f.adjoint := by simp [lTensor] + (f.lTensor G).adjoint = f.adjoint.lTensor G := by simp [lTensor] /-- Given `x, y : E ⊗ (F ⊗ G)`, `x = y` iff `⟪x, a ⊗ₜ (b ⊗ₜ c)⟫ = ⟪y, a ⊗ₜ (b ⊗ₜ c)⟫` for all `a, b, c`. diff --git a/Mathlib/Analysis/LocallyConvex/AbsConvexOpen.lean b/Mathlib/Analysis/LocallyConvex/AbsConvexOpen.lean index 812c09cfba3a10..3ea3c4d5852ae0 100644 --- a/Mathlib/Analysis/LocallyConvex/AbsConvexOpen.lean +++ b/Mathlib/Analysis/LocallyConvex/AbsConvexOpen.lean @@ -100,7 +100,7 @@ theorem gaugeSeminormFamily_ball (s : AbsConvexOpenSets 𝕜 E) : dsimp only [gaugeSeminormFamily] rw [Seminorm.ball_zero_eq] simp_rw [gaugeSeminorm_toFun] - exact gauge_lt_one_eq_self_of_isOpen (s.coe_convex.lift ℝ) s.coe_zero_mem s.coe_isOpen + exact setOf_gauge_lt_one_eq_self_of_isOpen (s.coe_convex.lift ℝ) s.coe_zero_mem s.coe_isOpen variable [IsTopologicalAddGroup E] [ContinuousSMul 𝕜 E] variable [LocallyConvexSpace 𝕜 E] diff --git a/Mathlib/Analysis/LocallyConvex/Basic.lean b/Mathlib/Analysis/LocallyConvex/Basic.lean index 33fa61bc088e4b..cdfd69065a23f7 100644 --- a/Mathlib/Analysis/LocallyConvex/Basic.lean +++ b/Mathlib/Analysis/LocallyConvex/Basic.lean @@ -305,4 +305,8 @@ theorem balanced_iff_neg_mem (hs : Convex ℝ s) : Balanced ℝ s ↔ ∀ ⦃x exact hs (h hx) hx (div_nonneg (sub_nonneg_of_le ha.2) zero_le_two) (div_nonneg (sub_nonneg_of_le ha.1) zero_le_two) (by ring) +theorem Balanced.starConvex (hs : Balanced ℝ s) : StarConvex ℝ 0 s := + starConvex_zero_iff.2 fun _ hx a ha₀ ha₁ => + hs _ (by rwa [Real.norm_of_nonneg ha₀]) (smul_mem_smul_set hx) + end Real diff --git a/Mathlib/Analysis/LocallyConvex/HahnBanach.lean b/Mathlib/Analysis/LocallyConvex/HahnBanach.lean index 22754bba9a8f0f..1082d18d304e88 100644 --- a/Mathlib/Analysis/LocallyConvex/HahnBanach.lean +++ b/Mathlib/Analysis/LocallyConvex/HahnBanach.lean @@ -68,8 +68,8 @@ variable [TopologicalSpace E] /-- **Hahn-Banach theorem** for linear functionals dominated by a continuous seminorm on polynormable spaces over `ℝ`. -/ -theorem Module.Dual.exists_continuous_extension_of_le_seminorm_real [IsTopologicalAddGroup E] - [Module ℝ E] [ContinuousSMul ℝ E] [PolynormableSpace ℝ E] (S : Subspace ℝ E) (f : Dual ℝ S) +theorem Module.Dual.exists_continuous_extension_of_le_seminorm_real + [Module ℝ E] [PolynormableSpace ℝ E] (S : Subspace ℝ E) (f : Dual ℝ S) {p : Seminorm ℝ E} (hp_cont : Continuous p) (hp : ∀ x, f x ≤ p x) : ∃ g : StrongDual ℝ E, (∀ x : S, g x = f x) ∧ ∀ x, |g x| ≤ p x := by obtain ⟨g, hg, hl⟩ := f.exists_extension_of_le_seminorm_real S hp diff --git a/Mathlib/Analysis/Matrix/Order.lean b/Mathlib/Analysis/Matrix/Order.lean index e07208296f2cec..20eec4b66f66bc 100644 --- a/Mathlib/Analysis/Matrix/Order.lean +++ b/Mathlib/Analysis/Matrix/Order.lean @@ -12,6 +12,7 @@ public import Mathlib.Analysis.Matrix.PosDef public import Mathlib.Analysis.RCLike.Sqrt public import Mathlib.Analysis.SpecialFunctions.ContinuousFunctionalCalculus.Abs public import Mathlib.LinearAlgebra.Matrix.Vec +public import Mathlib.Analysis.CStarAlgebra.Matrix /-! # The partial order on matrices @@ -339,4 +340,28 @@ def toMatrixInnerProductSpace (M : Matrix n n 𝕜) (hM : M.PosSemidef) : @[deprecated (since := "2025-11-18")] alias PosDef.matrixNormedAddCommGroup := toMatrixNormedAddCommGroup + +open scoped Norms.L2Operator in +set_option backward.isDefEq.respectTransparency false in +/-- The isometric continuous functional calculus on `Matrix n n 𝕜` arising from the operator norm +given by the identification with (continuous) linear endomorphisms of `EuclideanSpace 𝕜 n`. -/ +instance instIsometricContinuousFunctionalCalculus [DecidableEq n] : + IsometricContinuousFunctionalCalculus ℝ (Matrix n n 𝕜) IsSelfAdjoint where + isometric A hA := by + rw [← isHermitian_iff_isSelfAdjoint] at hA + rw [IsHermitian.cfcHom_eq_cfcAux hA, AddMonoidHomClass.isometry_iff_norm] + intro f + simp only [IsHermitian.cfcAux_apply, Unitary.conjStarAlgAut_apply, ← Unitary.coe_star, + CStarRing.norm_mul_coe_unitary, CStarRing.norm_coe_unitary_mul, l2_opNorm_diagonal] + rw [((algebraMap_isometry ℝ 𝕜).postcomp_pi).norm_map_of_map_zero (by ext; simp)] + let : Fintype (spectrum ℝ A) := .ofFinite _ + rw [ContinuousMap.norm_eq_norm_coeFn] + refine Function.Surjective.pi_norm_comp ?_ _ + rw [← Function.Surjective.of_comp_iff' + (Equiv.setCongr hA.spectrum_real_eq_range_eigenvalues).bijective] + exact Set.codRestrict_range_surjective hA.eigenvalues + +scoped[Matrix.Norms.L2Operator] attribute [instance] + Matrix.instIsometricContinuousFunctionalCalculus + end Matrix diff --git a/Mathlib/Analysis/MellinInversion.lean b/Mathlib/Analysis/MellinInversion.lean index 6fdc84bdf207bc..26d5ba10ed9d78 100644 --- a/Mathlib/Analysis/MellinInversion.lean +++ b/Mathlib/Analysis/MellinInversion.lean @@ -112,7 +112,7 @@ theorem mellinInv_mellin_eq (σ : ℝ) (f : ℝ → E) {x : ℝ} (hx : 0 < x) (h simp_rw [neg_mul_eq_neg_mul] at this exact this replace hfx : ContinuousAt g (-Real.log x) := by - refine ContinuousAt.smul (by fun_prop) (ContinuousAt.comp ?_ (by fun_prop)) + refine ContinuousAt.fun_smul (by fun_prop) (ContinuousAt.comp ?_ (by fun_prop)) simpa [Real.exp_log hx] using hfx calc mellinInv σ (mellin f) x diff --git a/Mathlib/Analysis/Meromorphic/Basic.lean b/Mathlib/Analysis/Meromorphic/Basic.lean index 76db272de7420a..af4ae3fc44673f 100644 --- a/Mathlib/Analysis/Meromorphic/Basic.lean +++ b/Mathlib/Analysis/Meromorphic/Basic.lean @@ -29,6 +29,7 @@ open scoped Topology variable {𝕜 𝕜' : Type*} [NontriviallyNormedField 𝕜] [NontriviallyNormedField 𝕜'] [NormedAlgebra 𝕜 𝕜'] {E : Type*} [NormedAddCommGroup E] [NormedSpace 𝕜 E] +variable {R : Type*} [NormedRing R] [Module R E] [IsBoundedSMul R E] [SMulCommClass 𝕜 R E] /-- Meromorphy of `f` at `x` (more precisely, on a punctured neighbourhood of `x`; the value at `x` itself is irrelevant). -/ @@ -94,6 +95,12 @@ lemma smul {f : 𝕜 → 𝕜} {g : 𝕜 → E} (hf : MeromorphicAt f x) (hg : M simp module +@[to_fun (attr := fun_prop)] +lemma const_smul {x : 𝕜} {f : 𝕜 → E} (hf : MeromorphicAt f x) (c : R) : + MeromorphicAt (c • f) x := by + rcases hf with ⟨m, hf⟩ + exact ⟨m, by simpa [smul_comm _ c _] using hf.fun_const_smul⟩ + @[to_fun (attr := fun_prop)] lemma mul {f g : 𝕜 → 𝕜'} (hf : MeromorphicAt f x) (hg : MeromorphicAt g x) : MeromorphicAt (f * g) x := by @@ -550,6 +557,9 @@ include hf in MeromorphicOn (s • f) U := fun x hx ↦ (hs x hx).smul (hf x hx) +include hf in +@[to_fun] lemma const_smul (c : R) : MeromorphicOn (c • f) U := fun x hx ↦ (hf x hx).const_smul c + include hs ht in @[to_fun] lemma mul : MeromorphicOn (s * t) U := fun x hx ↦ (hs x hx).mul (ht x hx) @@ -695,6 +705,10 @@ lemma sub (hf : Meromorphic f) (hg : Meromorphic g) : lemma smul {f : 𝕜 → 𝕜} (hf : Meromorphic f) (hg : Meromorphic g) : Meromorphic (f • g) := fun x ↦ (hf x).smul (hg x) +@[to_fun (attr := fun_prop)] +lemma const_smul (hf : Meromorphic f) (c : R) : + Meromorphic (c • f) := fun x ↦ (hf x).const_smul c + @[to_fun (attr := fun_prop)] lemma mul {f g : 𝕜 → 𝕜'} (hf : Meromorphic f) (hg : Meromorphic g) : Meromorphic (f * g) := fun x ↦ (hf x).mul (hg x) diff --git a/Mathlib/Analysis/Meromorphic/Order.lean b/Mathlib/Analysis/Meromorphic/Order.lean index fef6d7fd10eb99..dd421392c0de3e 100644 --- a/Mathlib/Analysis/Meromorphic/Order.lean +++ b/Mathlib/Analysis/Meromorphic/Order.lean @@ -892,3 +892,43 @@ lemma meromorphicOrderAt_mul_of_ne_zero {f : 𝕜 → 𝕜} (hg : AnalyticAt meromorphicOrderAt_smul_of_ne_zero hg hg' end smul + +/-! +## Order at a Point of the Derivative +-/ + +section deriv + +/-- The meromorphic order of the derivative is one less than the order of the original function. +This however is not true if the characteristic of the domain field divides the original order, +where the order of the derivative can rise to a larger integer. -/ +lemma meromorphicOrderAt_deriv_eq_sub_one [CompleteSpace E] {f : 𝕜 → E} {x : 𝕜} {n : ℤ} + (hn : (n : 𝕜) ≠ 0) (hf : meromorphicOrderAt f x = ↑n) : + meromorphicOrderAt (deriv f) x = ↑(n - 1) := by + have hmero : MeromorphicAt f x := meromorphicAt_of_meromorphicOrderAt_ne_zero (by aesop) + rw [meromorphicOrderAt_eq_int_iff hmero] at hf + rw [meromorphicOrderAt_eq_int_iff hmero.deriv] + obtain ⟨g, hga, hg0, (hg : f =ᶠ[𝓝[≠] x] fun z ↦ (z - x) ^ n • g z)⟩ := hf + refine ⟨fun z ↦ (n : 𝕜) • g z + (z - x) • deriv g z, by fun_prop, by simpa using ⟨hn, hg0⟩, ?_⟩ + filter_upwards [hga.eventually_analyticAt.filter_mono (nhdsWithin_le_nhds), + eventually_mem_nhdsWithin, hg.nhdsNE_deriv] with z hgz hmem hz + have hzx : z - x ≠ 0 := by simpa [sub_eq_zero] using hmem + calc + deriv f z = deriv (fun z ↦ (z - x) ^ n • g z) z := + hz + _ = (z - x) ^ n • deriv g z + deriv ((· ^ n) ∘ (· - x)) z • g z := + deriv_fun_smul (by fun_prop (disch := grind)) hgz.differentiableAt + _ = (z - x) ^ n • deriv g z + (n * (z - x) ^ (n - 1)) • g z := by + rw [deriv_comp _ (by fun_prop (disch := grind)) (by fun_prop)] + simp [deriv_zpow] + _ = (z - x) ^ (n - 1) • ((n : 𝕜) • g z + (z - x) • deriv g z) := by + simp [smul_smul, ← zpow_add_one₀ hzx, add_comm, mul_comm] + +/-- Equivalent to `meromorphicOrderAt_deriv_eq_sub_one` with a slightly different statement so the +conclusion matches more targets -/ +lemma meromorphicOrderAt_deriv [CompleteSpace E] {f : 𝕜 → E} {x : 𝕜} {n : ℤ} + (hn : (↑(n + 1) : 𝕜) ≠ 0) (hf : meromorphicOrderAt f x = ↑(n + 1)) : + meromorphicOrderAt (deriv f) x = ↑n := by + simpa using meromorphicOrderAt_deriv_eq_sub_one hn hf + +end deriv diff --git a/Mathlib/Analysis/Normed/Affine/Convex.lean b/Mathlib/Analysis/Normed/Affine/Convex.lean index 301069917cad6a..16bebba8cb4ea2 100644 --- a/Mathlib/Analysis/Normed/Affine/Convex.lean +++ b/Mathlib/Analysis/Normed/Affine/Convex.lean @@ -12,6 +12,7 @@ public import Mathlib.Analysis.Normed.Module.Convex /-! # Simplices in normed affine spaces + We prove the following facts: * `exists_mem_interior_convexHull_affineBasis` : We can intercalate a simplex between a point and diff --git a/Mathlib/Analysis/Normed/Algebra/Spectrum.lean b/Mathlib/Analysis/Normed/Algebra/Spectrum.lean index eb2a4e773f4a14..152771a3d46fe5 100644 --- a/Mathlib/Analysis/Normed/Algebra/Spectrum.lean +++ b/Mathlib/Analysis/Normed/Algebra/Spectrum.lean @@ -20,7 +20,7 @@ public import Mathlib.Topology.Semicontinuity.Hemicontinuity This file contains the basic theory for the resolvent and spectrum of a Banach algebra. Theorems specific to *complex* Banach algebras, such as *Gelfand's formula* can be found in - `Mathlib/Analysis/Normed/Algebra/GelfandFormula.lean`. +`Mathlib/Analysis/Normed/Algebra/GelfandFormula.lean`. ## Main definitions diff --git a/Mathlib/Analysis/Normed/Field/Basic.lean b/Mathlib/Analysis/Normed/Field/Basic.lean index 9a0b0745f1b610..5b987828d26609 100644 --- a/Mathlib/Analysis/Normed/Field/Basic.lean +++ b/Mathlib/Analysis/Normed/Field/Basic.lean @@ -43,6 +43,9 @@ class NormedDivisionRing (α : Type*) extends Norm α, DivisionRing α, MetricSp /-- The norm is multiplicative. -/ protected norm_mul : ∀ a b, norm (a * b) = norm a * norm b +-- see Note [lower instance priority] +attribute [instance 10] NormedDivisionRing.toDivisionRing + -- see Note [lower instance priority] /-- A normed division ring is a normed ring. -/ instance (priority := 100) NormedDivisionRing.toNormedRing [β : NormedDivisionRing α] : @@ -154,6 +157,9 @@ class NormedField (α : Type*) extends Norm α, Field α, MetricSpace α where /-- The norm is multiplicative. -/ protected norm_mul : ∀ a b, norm (a * b) = norm a * norm b +-- see Note [lower instance priority] +attribute [instance 10] NormedField.toField + /-- A nontrivially normed field is a normed field in which there is an element of norm different from `0` and `1`. This makes it possible to bring any element arbitrarily close to `0` by multiplication by the powers of any element, and thus to relate algebra and topology. -/ diff --git a/Mathlib/Analysis/Normed/Group/Basic.lean b/Mathlib/Analysis/Normed/Group/Basic.lean index 0409bfc27bfc7b..f0789e63185df6 100644 --- a/Mathlib/Analysis/Normed/Group/Basic.lean +++ b/Mathlib/Analysis/Normed/Group/Basic.lean @@ -1069,17 +1069,17 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: multiplicative norms are always nonnegative, and positive on non-one inputs. -/ @[positivity ‖_‖] -meta def evalMulNorm : PositivityExt where eval {u α} _ pα? e := do +meta def evalMulNorm : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@Norm.norm $E $_n $a) => - let some _ := pα? | pure .none let _seminormedGroup_E ← synthInstanceQ q(SeminormedGroup $E) assertInstancesCommute -- Check whether we are in a normed group and whether the context contains a `a ≠ 1` assumption - let o : Option (Q(NormedGroup $E) × Q($a ≠ 1)) := ← do - let .some normedGroup_E ← trySynthInstanceQ q(NormedGroup $E) | return none - let some pa ← findLocalDeclWithTypeQ? q($a ≠ 1) | return none - return some (normedGroup_E, pa) + let o : Option (Q(NormedGroup $E) × Q($a ≠ 1)) ← do + let .some normedGroup_E ← trySynthInstanceQ q(NormedGroup $E) | pure none + let some pa ← findLocalDeclWithTypeQ? q($a ≠ 1) | pure none + pure <| some (normedGroup_E, pa) match o with -- If so, return a proof of `0 < ‖a‖` | some (_normedGroup_E, pa) => @@ -1092,17 +1092,17 @@ meta def evalMulNorm : PositivityExt where eval {u α} _ pα? e := do /-- Extension for the `positivity` tactic: additive norms are always nonnegative, and positive on non-zero inputs. -/ @[positivity ‖_‖] -meta def evalAddNorm : PositivityExt where eval {u α} _ pα? e := do +meta def evalAddNorm : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@Norm.norm $E $_n $a) => - let some _ := pα? | pure .none let _seminormedAddGroup_E ← synthInstanceQ q(SeminormedAddGroup $E) assertInstancesCommute -- Check whether we are in a normed group and whether the context contains a `a ≠ 0` assumption - let o : Option (Q(NormedAddGroup $E) × Q($a ≠ 0)) := ← do - let .some normedAddGroup_E ← trySynthInstanceQ q(NormedAddGroup $E) | return none - let some pa ← findLocalDeclWithTypeQ? q($a ≠ 0) | return none - return some (normedAddGroup_E, pa) + let o : Option (Q(NormedAddGroup $E) × Q($a ≠ 0)) ← do + let .some normedAddGroup_E ← trySynthInstanceQ q(NormedAddGroup $E) | pure none + let some pa ← findLocalDeclWithTypeQ? q($a ≠ 0) | pure none + pure <| some (normedAddGroup_E, pa) match o with -- If so, return a proof of `0 < ‖a‖` | some (_normedAddGroup_E, pa) => diff --git a/Mathlib/Analysis/Normed/Group/Constructions.lean b/Mathlib/Analysis/Normed/Group/Constructions.lean index 6ce5fd0c8b9fd0..030fcae76666bb 100644 --- a/Mathlib/Analysis/Normed/Group/Constructions.lean +++ b/Mathlib/Analysis/Normed/Group/Constructions.lean @@ -366,6 +366,31 @@ lemma pi_norm_const' [Nonempty ι] (a : E) : ‖fun _i : ι => a‖ = ‖a‖ := lemma pi_nnnorm_const' [Nonempty ι] (a : E) : ‖fun _i : ι => a‖₊ = ‖a‖₊ := NNReal.eq <| pi_norm_const' a +@[to_additive pi_norm_comp_le] +lemma pi_norm_comp_le' [Fintype F] (g : ι → E) (f : F → ι) : ‖g ∘ f‖ ≤ ‖g‖ := by + rw [pi_norm_le_iff_of_nonneg' (by positivity)] + exact fun x ↦ norm_le_pi_norm' g (f x) + +@[to_additive IsGreatest.pi_norm] +lemma IsGreatest.pi_norm' [Nonempty ι] (f : ι → E) : IsGreatest (Set.range (‖f ·‖)) ‖f‖ := by + constructor + · rw [Pi.norm_def' f] + obtain ⟨x, -, hx⟩ := (Finset.univ (α := ι)).exists_mem_eq_sup (by simp) (‖f ·‖₊) + simp [hx] + · rintro - ⟨x, rfl⟩ + exact norm_le_pi_norm' f x + +@[to_additive Function.Surjective.pi_norm_comp] +lemma Function.Surjective.pi_norm_comp' [Fintype F] {f : ι → F} (hf : Function.Surjective f) + (g : F → E) : ‖g ∘ f‖ = ‖g‖ := by + obtain (h | h) := isEmpty_or_nonempty F + · have : IsEmpty ι := f.isEmpty + simp [Subsingleton.elim g 1] + apply le_antisymm (pi_norm_comp_le' g f) + obtain ⟨⟨x, h⟩, -⟩ := IsGreatest.pi_norm' g + obtain ⟨y, rfl⟩ := hf x + exact h ▸ norm_le_pi_norm' (g ∘ f) y + /-- The $L^1$ norm is less than the $L^\infty$ norm scaled by the cardinality. -/ @[to_additive Pi.sum_norm_apply_le_norm /-- The $L^1$ norm is less than the $L^\infty$ norm scaled by the cardinality. -/] diff --git a/Mathlib/Analysis/Normed/Group/Continuity.lean b/Mathlib/Analysis/Normed/Group/Continuity.lean index 609c44b07dde86..e4e9048e9eaae7 100644 --- a/Mathlib/Analysis/Normed/Group/Continuity.lean +++ b/Mathlib/Analysis/Normed/Group/Continuity.lean @@ -7,6 +7,7 @@ module public import Mathlib.Analysis.Normed.Group.Basic public import Mathlib.Topology.Algebra.Ring.Real +public import Mathlib.Topology.Instances.ENNReal.Lemmas public import Mathlib.Topology.Metrizable.Uniformity public import Mathlib.Topology.Sequences @@ -68,6 +69,16 @@ theorem tendsto_one_iff_norm_tendsto_zero {f : α → E} {a : Filter α} : Tendsto f a (𝓝 1) ↔ Tendsto (‖f ·‖) a (𝓝 0) := tendsto_iff_norm_inv_mul_tendsto_zero.trans <| by simp +@[to_additive] +theorem tendsto_iff_enorm_inv_mul_tendsto_zero {f : α → E} {a : Filter α} {b : E} : + Tendsto f a (𝓝 b) ↔ Tendsto (fun e => ‖(f e)⁻¹ * b‖ₑ) a (𝓝 0) := by + simp only [← edist_eq_enorm_inv_mul, ← tendsto_iff_edist_tendsto_0] + +@[to_additive] +theorem tendsto_one_iff_enorm_tendsto_zero {f : α → E} {a : Filter α} : + Tendsto f a (𝓝 1) ↔ Tendsto (‖f ·‖ₑ) a (𝓝 0) := + tendsto_iff_enorm_inv_mul_tendsto_zero.trans <| by simp + @[to_additive (attr := simp 1100)] theorem comap_norm_nhds_one : comap norm (𝓝 0) = 𝓝 (1 : E) := by simpa only [dist_one_right] using nhds_comap_dist (1 : E) @@ -302,6 +313,11 @@ theorem tendsto_iff_norm_div_tendsto_zero {f : α → E} {a : Filter α} {b : E} Tendsto f a (𝓝 b) ↔ Tendsto (fun e => ‖f e / b‖) a (𝓝 0) := by simp only [← dist_eq_norm_div, ← tendsto_iff_dist_tendsto_zero] +@[to_additive] +theorem tendsto_iff_enorm_div_tendsto_zero {f : α → E} {a : Filter α} {b : E} : + Tendsto f a (𝓝 b) ↔ Tendsto (fun e => ‖f e / b‖ₑ) a (𝓝 0) := by + simp only [← edist_eq_enorm_div, ← tendsto_iff_edist_tendsto_0] + @[to_additive] theorem SeminormedCommGroup.mem_closure_iff {s : Set E} : a ∈ closure s ↔ ∀ ε, 0 < ε → ∃ b ∈ s, ‖a / b‖ < ε := by diff --git a/Mathlib/Analysis/Normed/Group/Defs.lean b/Mathlib/Analysis/Normed/Group/Defs.lean index 986529b3cc34e4..91d2e8006fe908 100644 --- a/Mathlib/Analysis/Normed/Group/Defs.lean +++ b/Mathlib/Analysis/Normed/Group/Defs.lean @@ -117,7 +117,7 @@ class ESeminormedAddMonoid (E : Type*) [TopologicalSpace E] protected enorm_add_le : ∀ x y : E, ‖x + y‖ₑ ≤ ‖x‖ₑ + ‖y‖ₑ -- see Note [lower instance priority] -attribute [instance 200] ESeminormedAddMonoid.toAddMonoid +attribute [instance 10] ESeminormedAddMonoid.toAddMonoid /-- An enormed monoid is an additive monoid endowed with a continuous enorm, which is positive definite: in other words, this is an `ESeminormedAddMonoid` with a positive @@ -134,7 +134,7 @@ class ESeminormedMonoid (E : Type*) [TopologicalSpace E] extends ContinuousENorm enorm_mul_le : ∀ x y : E, ‖x * y‖ₑ ≤ ‖x‖ₑ + ‖y‖ₑ -- see Note [lower instance priority] -attribute [instance 200] ESeminormedMonoid.toMonoid +attribute [instance 10] ESeminormedMonoid.toMonoid /-- An enormed monoid is a monoid endowed with a continuous enorm, which is positive definite: in other words, this is an `ESeminormedMonoid` with a positive @@ -153,7 +153,7 @@ class ESeminormedAddCommMonoid (E : Type*) [TopologicalSpace E] extends ESeminormedAddMonoid E, AddCommMonoid E where -- see Note [lower instance priority] -attribute [instance 200] ESeminormedAddCommMonoid.toAddCommMonoid +attribute [instance 10] ESeminormedAddCommMonoid.toAddCommMonoid /-- An enormed commutative monoid is an additive commutative monoid endowed with a continuous enorm which is positive definite. @@ -170,7 +170,7 @@ class ESeminormedCommMonoid (E : Type*) [TopologicalSpace E] extends ESeminormedMonoid E, CommMonoid E where -- see Note [lower instance priority] -attribute [instance 200] ESeminormedCommMonoid.toCommMonoid +attribute [instance 10] ESeminormedCommMonoid.toCommMonoid /-- An enormed commutative monoid is a commutative monoid endowed with a continuous enorm which is positive definite. -/ @@ -186,7 +186,7 @@ class SeminormedAddGroup (E : Type*) extends Norm E, AddGroup E, PseudoMetricSpa dist_eq : ∀ x y, dist x y = ‖-x + y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] SeminormedAddGroup.toAddGroup +attribute [instance 10] SeminormedAddGroup.toAddGroup /-- A seminormed group is a group endowed with a norm for which `dist x y = ‖x⁻¹ * y‖` defines a pseudometric space structure. -/ @@ -197,7 +197,7 @@ class SeminormedGroup (E : Type*) extends Norm E, Group E, PseudoMetricSpace E w dist_eq : ∀ x y, dist x y = ‖x⁻¹ * y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] SeminormedGroup.toGroup +attribute [instance 10] SeminormedGroup.toGroup /-- A normed group is an additive group endowed with a norm for which `dist x y = ‖-x + y‖` defines a metric space structure. -/ @@ -207,7 +207,7 @@ class NormedAddGroup (E : Type*) extends Norm E, AddGroup E, MetricSpace E where dist_eq : ∀ x y, dist x y = ‖-x + y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] NormedAddGroup.toAddGroup +attribute [instance 10] NormedAddGroup.toAddGroup /-- A normed group is a group endowed with a norm for which `dist x y = ‖x⁻¹ * y‖` defines a metric space structure. -/ @@ -218,7 +218,7 @@ class NormedGroup (E : Type*) extends Norm E, Group E, MetricSpace E where dist_eq : ∀ x y, dist x y = ‖x⁻¹ * y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] NormedGroup.toGroup +attribute [instance 10] NormedGroup.toGroup /-- A seminormed group is an additive group endowed with a norm for which `dist x y = ‖-x + y‖` defines a pseudometric space structure. -/ @@ -229,7 +229,7 @@ class SeminormedAddCommGroup (E : Type*) extends Norm E, AddCommGroup E, dist_eq : ∀ x y, dist x y = ‖-x + y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] SeminormedAddCommGroup.toAddCommGroup +attribute [instance 10] SeminormedAddCommGroup.toAddCommGroup /-- A seminormed group is a group endowed with a norm for which `dist x y = ‖x⁻¹ * y‖` defines a pseudometric space structure. -/ @@ -240,7 +240,7 @@ class SeminormedCommGroup (E : Type*) extends Norm E, CommGroup E, PseudoMetricS dist_eq : ∀ x y, dist x y = ‖x⁻¹ * y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] SeminormedCommGroup.toCommGroup +attribute [instance 10] SeminormedCommGroup.toCommGroup /-- A normed group is an additive group endowed with a norm for which `dist x y = ‖-x + y‖` defines a metric space structure. -/ @@ -250,7 +250,7 @@ class NormedAddCommGroup (E : Type*) extends Norm E, AddCommGroup E, MetricSpace dist_eq : ∀ x y, dist x y = ‖-x + y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] NormedAddCommGroup.toAddCommGroup +attribute [instance 10] NormedAddCommGroup.toAddCommGroup /-- A normed group is a group endowed with a norm for which `dist x y = ‖x⁻¹ * y‖` defines a metric space structure. -/ @@ -261,7 +261,7 @@ class NormedCommGroup (E : Type*) extends Norm E, CommGroup E, MetricSpace E whe dist_eq : ∀ x y, dist x y = ‖x⁻¹ * y‖ := by aesop -- see Note [lower instance priority] -attribute [instance 200] NormedCommGroup.toCommGroup +attribute [instance 10] NormedCommGroup.toCommGroup -- See note [lower instance priority] @[to_additive] diff --git a/Mathlib/Analysis/Normed/Lp/Finsupp.lean b/Mathlib/Analysis/Normed/Lp/Finsupp.lean new file mode 100644 index 00000000000000..b9ae2f1afb8f32 --- /dev/null +++ b/Mathlib/Analysis/Normed/Lp/Finsupp.lean @@ -0,0 +1,102 @@ +/- +Copyright (c) 2026 Yaël Dillies. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Yaël Dillies +-/ +module + +public import Mathlib.Algebra.BigOperators.Finsupp.Basic +public import Mathlib.Analysis.Normed.Lp.WithLp +public import Mathlib.Analysis.SpecialFunctions.Pow.NNReal +public import Mathlib.Topology.MetricSpace.Basic + +import Mathlib.Algebra.Order.BigOperators.Group.Finset +import Mathlib.Analysis.MeanInequalities +import Mathlib.Data.ENNReal.BigOperators +import Mathlib.Tactic.Positivity.Finset + +/-! +# Direct sum of metric spaces + +This files endows the direct sum `ι →₀ X` of `ι`-many copies of a metric space `X` with the +L^p metric. + +## TODO + +Allow the L^∞ metric too. Currently, there is no easy way to perform the proofs: +`match` on `ℝ≥0∞` exposes the underlying `Option` and `induction p using ENNReal.recTopCoe` in the +`EMetricSpace` instance chokes on the `PseudoEMetricSpace` one. +-/ + +open scoped ENNReal NNReal + +public section + +namespace Finsupp +variable {ι X : Type*} [Zero X] {p : ℝ≥0} [Fact (1 ≤ p)] + +/-- The L^1 extended metric on `ι`-many copies of a metric space `X` -/ +noncomputable instance [PseudoEMetricSpace X] : PseudoEMetricSpace (WithLp p <| ι →₀ X) where + edist f g := + ((f.ofLp.zipWith edist (edist_self _) g.ofLp).sum fun i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ) + edist_self f := by + have : 0 < p := zero_lt_one.trans_le Fact.out + simp [sum, *] + edist_comm f g := by + simp only [sum, zipWith_apply, edist_comm] + congr 2 + ext i + simp [edist_comm] + edist_triangle f g h := by + classical + have : 0 < p := zero_lt_one.trans_le Fact.out + let s := f.ofLp.support ∪ g.ofLp.support ∪ h.ofLp.support + rw [sum_of_support_subset (s := s) _ (by grind [support_zipWith]) _ (by simp [*]), + sum_of_support_subset (s := s) _ (by grind [support_zipWith]) _ (by simp [*]), + sum_of_support_subset (s := s) _ (by grind [support_zipWith]) _ (by simp [*])] + simp only [zipWith_apply, ← one_div] + grw [← ENNReal.Lp_add_le _ _ _ (mod_cast Fact.out)] + gcongr + exact edist_triangle .. + +lemma edist_def [PseudoEMetricSpace X] {p : ℝ≥0} [Fact (1 ≤ p)] + (f g : WithLp p <| ι →₀ X) : + edist f g = + ((f.ofLp.zipWith edist (edist_self _) g.ofLp).sum fun _i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ) := rfl + +/-- The L^1 extended metric on `ι`-many copies of a metric space `X` -/ +noncomputable instance [EMetricSpace X] : EMetricSpace (WithLp p <| ι →₀ X) where + eq_of_edist_eq_zero {f g} hfg := by simp_all [edist_def, sum, WithLp.ext_iff, DFunLike.ext_iff] + +/-- The L^1 metric on `ι`-many copies of a metric space `X` -/ +noncomputable instance [PseudoMetricSpace X] : PseudoMetricSpace (WithLp p <| ι →₀ X) := + PseudoEMetricSpace.toPseudoMetricSpaceOfDist + (fun f g ↦ ((f.ofLp.zipWith dist (dist_self _) g.ofLp).sum fun i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ)) + (fun f g ↦ by dsimp [sum]; positivity) fun f g ↦ by + simp only [edist_def, sum, zipWith_apply, ← coe_nnreal_ennreal_nndist, NNReal.zero_le_coe, + ← ENNReal.coe_rpow_of_nonneg, ← ENNReal.ofNNReal_finsetSum, inv_nonneg, ← coe_nndist, + ← NNReal.coe_rpow, ← NNReal.coe_sum, ENNReal.ofReal_coe_nnreal, ENNReal.coe_inj] + congr! 2 + ext i + simp [← coe_nndist, ← coe_nnreal_ennreal_nndist] + +lemma dist_def [PseudoMetricSpace X] (f g : WithLp p <| ι →₀ X) : + dist f g = + ((f.ofLp.zipWith dist (dist_self _) g.ofLp).sum fun _i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ) := rfl + +lemma nndist_def [PseudoMetricSpace X] (f g : WithLp p <| ι →₀ X) : + nndist f g = + ((f.ofLp.zipWith nndist (nndist_self _) g.ofLp).sum fun _i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ) := by + ext + simp only [coe_nndist, dist_def, sum, zipWith_apply, NNReal.coe_sum, NNReal.coe_rpow] + congr 2 + ext i + simp [← coe_nndist] + +/-- The L^1 metric on `ι`-many copies of a metric space `X` -/ +noncomputable instance [MetricSpace X] : MetricSpace (WithLp p <| ι →₀ X) := + EMetricSpace.toMetricSpaceOfDist + (fun f g ↦ ((f.ofLp.zipWith dist (dist_self _) g.ofLp).sum fun i r ↦ r ^ (p : ℝ)) ^ (p⁻¹ : ℝ)) + (fun f g ↦ by dsimp [sum]; positivity) fun f g ↦ by rw [edist_dist, dist_def] + +end Finsupp diff --git a/Mathlib/Analysis/Normed/Lp/PiLp.lean b/Mathlib/Analysis/Normed/Lp/PiLp.lean index 5bc4fbdc2b7368..c82470b0c99e0b 100644 --- a/Mathlib/Analysis/Normed/Lp/PiLp.lean +++ b/Mathlib/Analysis/Normed/Lp/PiLp.lean @@ -12,6 +12,7 @@ public import Mathlib.Analysis.Normed.Lp.ProdLp /-! # `L^p` distance on finite products of metric spaces + Given finitely many metric spaces, one can put the max distance on their product, but there is also a whole family of natural distances, indexed by a parameter `p : ℝ≥0∞`, that also induce the product topology. We define them in this file. For `0 < p < ∞`, the distance on `Π i, α i` @@ -1129,13 +1130,13 @@ end Fintype section -variable [Semiring 𝕜] [∀ i, SeminormedAddCommGroup (β i)] [∀ i, Module 𝕜 (β i)] +variable [Semiring 𝕜] [∀ i, AddCommGroup (β i)] [∀ i, Module 𝕜 (β i)] [∀ i, TopologicalSpace (β i)] -set_option backward.defeqAttrib.useBackward true in /-- `WithLp.linearEquiv` as a continuous linear equivalence. -/ @[simps! apply symm_apply] def continuousLinearEquiv : PiLp p β ≃L[𝕜] ∀ i, β i where toLinearEquiv := WithLp.linearEquiv _ _ _ + continuous_invFun := (by fun_prop : Continuous fun (a : Π i, β i) ↦ toLp p a) lemma coe_continuousLinearEquiv : ⇑(PiLp.continuousLinearEquiv p 𝕜 β) = ofLp := rfl @@ -1143,6 +1144,18 @@ lemma coe_continuousLinearEquiv : lemma coe_symm_continuousLinearEquiv : ⇑(PiLp.continuousLinearEquiv p 𝕜 β).symm = toLp p := rfl +/-- The natural equivalence between `PiLp p β` and `β default`, +for any index type `ι` with a unique element. -/ +@[simps! apply symm_apply] +def equivOfUnique [Unique ι] : PiLp p β ≃L[𝕜] β default := + (continuousLinearEquiv p 𝕜 β).trans <| .piUnique 𝕜 β + +end + +section + +variable [Semiring 𝕜] [∀ i, NormedAddCommGroup (β i)] [∀ i, Module 𝕜 (β i)] + variable {𝕜} in /-- The projection on the `i`-th coordinate of `PiLp p β`, as a continuous linear map. -/ @[simps!] diff --git a/Mathlib/Analysis/Normed/Lp/ProdLp.lean b/Mathlib/Analysis/Normed/Lp/ProdLp.lean index c48b16bc11de1b..46c7a52ff50c8a 100644 --- a/Mathlib/Analysis/Normed/Lp/ProdLp.lean +++ b/Mathlib/Analysis/Normed/Lp/ProdLp.lean @@ -10,6 +10,7 @@ public import Mathlib.Analysis.Normed.Lp.WithLp /-! # `L^p` distance on products of two metric spaces + Given two metric spaces, one can put the max distance on their product, but there is also a whole family of natural distances, indexed by a parameter `p : ℝ≥0∞`, that also induce the product topology. We define them in this file. For `0 < p < ∞`, the distance on `α × β` diff --git a/Mathlib/Analysis/Normed/Lp/WithLp.lean b/Mathlib/Analysis/Normed/Lp/WithLp.lean index 847baeb70474e4..cbf50d3dfae4e3 100644 --- a/Mathlib/Analysis/Normed/Lp/WithLp.lean +++ b/Mathlib/Analysis/Normed/Lp/WithLp.lean @@ -101,6 +101,9 @@ variable {K V} lemma ofLp_toLp (x : V) : ofLp (toLp p x) = x := rfl @[simp] lemma toLp_ofLp (x : WithLp p V) : toLp p (ofLp x) = x := rfl +lemma ext_iff {x y : WithLp p V} : x = y ↔ x.ofLp = y.ofLp := + (WithLp.equiv p V).injective.eq_iff.symm + lemma ofLp_surjective : Function.Surjective (@ofLp p V) := Function.RightInverse.surjective <| ofLp_toLp _ diff --git a/Mathlib/Analysis/Normed/Lp/lpSpace.lean b/Mathlib/Analysis/Normed/Lp/lpSpace.lean index f5c101d5c96cc3..172f73b6945bf4 100644 --- a/Mathlib/Analysis/Normed/Lp/lpSpace.lean +++ b/Mathlib/Analysis/Normed/Lp/lpSpace.lean @@ -1243,8 +1243,7 @@ lemma toAddMonoidHom_linearMapOfLE (h : p ≤ q) : ext; rfl lemma linearMapOfLE_comp (hpq : p ≤ q) (hqr : q ≤ r) : - (linearMapOfLE 𝕜 E hqr).comp (linearMapOfLE 𝕜 E hpq) = - linearMapOfLE 𝕜 E (hpq.trans hqr) := by + (linearMapOfLE 𝕜 E hqr).comp (linearMapOfLE 𝕜 E hpq) = linearMapOfLE 𝕜 E (hpq.trans hqr) := by ext; rfl end OfLE diff --git a/Mathlib/Analysis/Normed/Module/ENormedSpace.lean b/Mathlib/Analysis/Normed/Module/ENormedSpace.lean deleted file mode 100644 index f67330db283dfc..00000000000000 --- a/Mathlib/Analysis/Normed/Module/ENormedSpace.lean +++ /dev/null @@ -1,5 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.Analysis.Normed.Group.Basic - -deprecated_module (since := "2025-12-18") diff --git a/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean b/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean index 16db21c902d403..aba6bf12d57fad 100644 --- a/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean +++ b/Mathlib/Analysis/Normed/Module/Multilinear/Basic.lean @@ -932,16 +932,15 @@ def flipMultilinear (f : G →L[𝕜] ContinuousMultilinearMap 𝕜 E G') : MultilinearMap.mkContinuous { toFun := fun m => LinearMap.mkContinuous - { toFun := fun x => f x m - map_add' := fun x y => by simp only [map_add, ContinuousMultilinearMap.add_apply] - map_smul' := fun c x => by - simp only [ContinuousMultilinearMap.smul_apply, map_smul, RingHom.id_apply] } + { toFun := (f · m) + map_add' := by simp + map_smul' := by simp } (‖f‖ * ∏ i, ‖m i‖) fun x => by rw [mul_right_comm] exact (f x).le_of_opNorm_le (f.le_opNorm x) _ map_update_add' := fun m i x y => by ext1 - simp only [_root_.add_apply, ContinuousMultilinearMap.map_update_add, LinearMap.coe_mk, + simp only [add_apply, ContinuousMultilinearMap.map_update_add, LinearMap.coe_mk, LinearMap.mkContinuous_apply, AddHom.coe_mk] map_update_smul' := fun m i c x => by ext1 diff --git a/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean b/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean index d85858c2bda0b0..b5269e984b5373 100644 --- a/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean +++ b/Mathlib/Analysis/Normed/Module/RCLike/Extend.lean @@ -5,7 +5,7 @@ Authors: Ruben Van de Velde -/ module -public import Mathlib.Analysis.Normed.Operator.Basic +public import Mathlib.Analysis.Normed.Operator.Mul public import Mathlib.Analysis.RCLike.Extend /-! @@ -38,6 +38,28 @@ theorem Module.Dual.norm_extendRCLike_le_seminorm [AddCommGroup E] [Module 𝕜 namespace StrongDual +/-- The extension `StrongDual.extendRCLike` as a continuous linear equivalence between +the strong duals when scalar multiplication (by `𝕜`) is jointly continuous. -/ +@[expose, simps! -isSimp apply symm_apply] +noncomputable def extendRCLikeL {𝕜 F : Type*} [RCLike 𝕜] [TopologicalSpace F] + [AddCommGroup F] [Module 𝕜 F] [ContinuousSMul 𝕜 F] [Module ℝ F] [IsScalarTower ℝ 𝕜 F] : + StrongDual ℝ F ≃L[ℝ] StrongDual 𝕜 F where + toLinearEquiv := extendRCLikeₗ + continuous_toFun := by + rw [(ContinuousLinearMap.isEmbedding_restrictScalars ℝ).continuous_iff] + let smulI : F →L[ℝ] F := (I : 𝕜) • ContinuousLinearMap.id 𝕜 F |>.restrictScalars ℝ + let mulI : 𝕜 →L[ℝ] 𝕜 := ContinuousLinearMap.mul ℝ 𝕜 (I : 𝕜) + exact ofRealCLM.postcomp F - mulI.postcomp F ∘L smulI.precomp 𝕜 ∘L ofRealCLM.postcomp F + |>.continuous + continuous_invFun := reCLM.postcomp F |>.continuous.comp <| + (ContinuousLinearMap.isEmbedding_restrictScalars ℝ).continuous + +@[simp] +lemma toLinearEquiv_extendRCLikeL {𝕜 F : Type*} [RCLike 𝕜] [TopologicalSpace F] + [AddCommGroup F] [Module 𝕜 F] [ContinuousSMul 𝕜 F] [Module ℝ F] [IsScalarTower ℝ 𝕜 F] : + (extendRCLikeL (𝕜 := 𝕜) (F := F)).toLinearEquiv = extendRCLikeₗ := + rfl + /-- If a continuous real-linear functional is bounded by a `𝕜`-seminorm, then its `𝕜`-linear extension is bounded by the same seminorm. -/ theorem norm_extendRCLike_le_seminorm [AddCommGroup E] [Module 𝕜 E] [Module ℝ E] @@ -70,6 +92,16 @@ noncomputable def extendRCLikeₗᵢ : StrongDual ℝ F ≃ₗᵢ[ℝ] StrongDua toLinearEquiv := StrongDual.extendRCLikeₗ norm_map' := norm_extendRCLike +@[simp] +lemma toLinearEquiv_extendRCLikeₗᵢ : + (extendRCLikeₗᵢ (𝕜 := 𝕜) (F := F)).toLinearEquiv = extendRCLikeₗ := + rfl + +@[simp] +lemma toContinuousLinearEquiv_extendRCLikeₗᵢ : + (extendRCLikeₗᵢ (F := F) (𝕜 := 𝕜)).toContinuousLinearEquiv = extendRCLikeL := + rfl + end StrongDual namespace ContinuousLinearMap diff --git a/Mathlib/Analysis/Normed/Module/WeakDual.lean b/Mathlib/Analysis/Normed/Module/WeakDual.lean index 857a24373b9937..99aeca6008b7bf 100644 --- a/Mathlib/Analysis/Normed/Module/WeakDual.lean +++ b/Mathlib/Analysis/Normed/Module/WeakDual.lean @@ -7,6 +7,7 @@ module public import Mathlib.Analysis.Normed.Module.Dual public import Mathlib.Analysis.Normed.Operator.Completeness +public import Mathlib.Analysis.Normed.Operator.Mul public import Mathlib.Topology.Algebra.Module.Spaces.WeakDual public import Mathlib.Topology.MetricSpace.PiNat public import Mathlib.Analysis.Normed.Operator.BanachSteinhaus @@ -370,3 +371,85 @@ theorem isSeqCompact_closedBall (x' : StrongDual 𝕜 E) (r : ℝ) : isSeqCompact_of_isBounded_of_isClosed 𝕜 _ (isBounded_closedBall x' r) (isClosed_closedBall x' r) end WeakDual + +section RCLike + +open RCLike +open scoped NNReal Topology + +namespace WeakDual + +-- we shadow the variables for this section because they don't fit with the rest of the file. +variable {α 𝕜 E F : Type*} [TopologicalSpace α] [RCLike 𝕜] + [AddCommGroup E] [Module 𝕜 E] [AddCommGroup F] [Module 𝕜 F] + +/-- A map into `WeakBilin (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜)` over `𝕜` (with `RCLike 𝕜`) is +continuous if the real parts of all the evaluation maps `a ↦ B (g a) y` are +continuous for each `y : F`. -/ +theorem _root_.WeakBilin.continuous_of_continuous_eval_re (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) + {g : α → WeakBilin B} (h : ∀ y, Continuous fun a ↦ re (B (g a) y)) : + Continuous g := by + refine WeakBilin.continuous_of_continuous_eval _ fun x ↦ ?_ + suffices Continuous fun a ↦ (re (B (g a) x) : 𝕜) - re (B (g a) ((I : 𝕜) • x)) * I by simpa + fun_prop + +variable [TopologicalSpace F] + +/-- A map into `WeakDual 𝕜 F` over `𝕜` (with `RCLike 𝕜`) is continuous if the real parts of all +the evaluation maps `a ↦ g a y` are continuous for each `y : F`. -/ +theorem continuous_of_continuous_eval_re {g : α → WeakDual 𝕜 F} + (h : ∀ x, Continuous fun a ↦ re (g a x)) : + Continuous g := + WeakBilin.continuous_of_continuous_eval_re _ h + +variable [ContinuousConstSMul 𝕜 F] [Module ℝ F] [IsScalarTower ℝ 𝕜 F] + +open StrongDual + +/-- The extension `StrongDual.extendRCLike` as a continuous linear equivalence between +the weak duals. -/ +@[simps! -isSimp apply symm_apply] +noncomputable def extendRCLikeL : WeakDual ℝ F ≃L[ℝ] WeakDual 𝕜 F where + toLinearEquiv := toStrongDual ≪≫ₗ extendRCLikeₗ ≪≫ₗ toWeakDual.restrictScalars ℝ + continuous_toFun := continuous_of_continuous_eval_re fun x ↦ by + simpa [extendRCLikeₗ_apply] using eval_continuous x + continuous_invFun := + continuous_of_continuous_eval fun x ↦ RCLike.continuous_re.comp (eval_continuous x) + +@[simp] +lemma toLinearEquiv_extendRCLikeL : + (extendRCLikeL (𝕜 := 𝕜) (F := F)).toLinearEquiv = + toStrongDual ≪≫ₗ extendRCLikeₗ ≪≫ₗ toWeakDual.restrictScalars ℝ := by + rfl + +lemma extendRCLikeL_apply_apply (f : WeakDual ℝ F) (x : F) : + extendRCLikeL (𝕜 := 𝕜) f x = f x - (I : 𝕜) • f ((I : 𝕜) • x) := by + rfl + +lemma extendRCLikeL_symm_apply_apply (f : WeakDual 𝕜 F) (x : F) : + extendRCLikeL.symm f x = re (f x) := + rfl + +@[simp] +lemma re_extendRCLikeL_apply_apply (f : WeakDual ℝ F) (x : F) : + re (extendRCLikeL (𝕜 := 𝕜) f x) = f x := by + simp [extendRCLikeL_apply_apply] + +@[simp] +lemma im_extendRCLikeL_apply_apply (f : WeakDual ℝ F) (x : F) : + im (extendRCLikeL (𝕜 := 𝕜) f x) = - f ((I : 𝕜) • x) := by + simp [extendRCLikeL_apply, extendRCLikeₗ_apply] + +@[simp high] +lemma toStrongDual_extendRCLikeL_apply (f : WeakDual ℝ F) : + (extendRCLikeL (𝕜 := 𝕜) f).toStrongDual = extendRCLikeₗ f := + rfl + +@[simp high] +lemma _root_.StrongDual.toWeakDual_extendRCLikeₗ_apply (f : StrongDual ℝ F) : + (extendRCLikeₗ f).toWeakDual = extendRCLikeL (𝕜 := 𝕜) f.toWeakDual := + rfl + +end WeakDual + +end RCLike diff --git a/Mathlib/Analysis/Normed/Operator/Basic.lean b/Mathlib/Analysis/Normed/Operator/Basic.lean index ce13673e1274a1..ada43ae7b633c5 100644 --- a/Mathlib/Analysis/Normed/Operator/Basic.lean +++ b/Mathlib/Analysis/Normed/Operator/Basic.lean @@ -412,26 +412,6 @@ variable [RingHomIsometric σ₁₂] (f : E →SL[σ₁₂] F) @[simp, nontriviality] theorem opNorm_subsingleton [Subsingleton E] : ‖f‖ = 0 := norm_of_subsingleton f -/-- The fundamental property of the operator norm, expressed with extended norms: -`‖f x‖ₑ ≤ ‖f‖ₑ * ‖x‖ₑ`. -/ -lemma le_opNorm_enorm (x : E) : ‖f x‖ₑ ≤ ‖f‖ₑ * ‖x‖ₑ := by - simp_rw [← ofReal_norm] - rw [← ENNReal.ofReal_mul (by positivity)] - gcongr - exact f.le_opNorm x - -/-- If one controls the enorm of every `f x`, then one controls the enorm of `f`. -/ -theorem opENorm_le_bound (f : E →SL[σ₁₂] F) {M : ℝ≥0∞} (hM : ∀ x, ‖f x‖ₑ ≤ M * ‖x‖ₑ) : - ‖f‖ₑ ≤ M := by - rcases eq_top_or_lt_top M with rfl | h'M - · simp - lift M to NNReal using h'M.ne - simp only [← ofReal_norm, ENNReal.ofReal_le_coe] - apply opNorm_le_bound _ (by positivity) (fun x ↦ ?_) - specialize hM x - simp only [← ofReal_norm, ← ENNReal.ofReal_coe_nnreal] at hM - rwa [← ENNReal.ofReal_mul (by positivity), ENNReal.ofReal_le_ofReal_iff (by positivity)] at hM - variable {f} in theorem homothety_norm [NontrivialTopology E] (f : E →SL[σ₁₂] F) {a : ℝ} (hf : ∀ x, ‖f x‖ = a * ‖x‖) : ‖f‖ = a := by diff --git a/Mathlib/Analysis/Normed/Operator/Bilinear.lean b/Mathlib/Analysis/Normed/Operator/Bilinear.lean index 187148928cc83d..d8d68e766caf5d 100644 --- a/Mathlib/Analysis/Normed/Operator/Bilinear.lean +++ b/Mathlib/Analysis/Normed/Operator/Bilinear.lean @@ -5,7 +5,7 @@ Authors: Jan-David Salchow, Sébastien Gouëzel, Jean Lo -/ module -public import Mathlib.Analysis.Normed.Operator.Basic +public import Mathlib.Analysis.Normed.Operator.NNNorm public import Mathlib.Analysis.Normed.Operator.LinearIsometry public import Mathlib.Analysis.Normed.Operator.ContinuousLinearMap @@ -59,24 +59,30 @@ theorem opNorm_ext [RingHomIsometric σ₁₃] (f : E →SL[σ₁₂] F) (g : E rw [← h z] exact h₂ z - variable [RingHomIsometric σ₂₃] theorem opNorm_le_bound₂ (f : E →SL[σ₁₃] F →SL[σ₂₃] G) {C : ℝ} (h0 : 0 ≤ C) (hC : ∀ x y, ‖f x y‖ ≤ C * ‖x‖ * ‖y‖) : ‖f‖ ≤ C := f.opNorm_le_bound h0 fun x => (f x).opNorm_le_bound (by positivity) <| hC x - theorem le_opNorm₂ [RingHomIsometric σ₁₃] (f : E →SL[σ₁₃] F →SL[σ₂₃] G) (x : E) (y : F) : ‖f x y‖ ≤ ‖f‖ * ‖x‖ * ‖y‖ := (f x).le_of_opNorm_le (f.le_opNorm x) y - theorem le_of_opNorm₂_le_of_le [RingHomIsometric σ₁₃] (f : E →SL[σ₁₃] F →SL[σ₂₃] G) {x : E} {y : F} {a b c : ℝ} (hf : ‖f‖ ≤ a) (hx : ‖x‖ ≤ b) (hy : ‖y‖ ≤ c) : ‖f x y‖ ≤ a * b * c := (f x).le_of_opNorm_le_of_le (f.le_of_opNorm_le_of_le hf hx) hy +open scoped ENNReal + +theorem opENorm_le_bound₂ [RingHomIsometric σ₁₃] (f : E →SL[σ₁₃] F →SL[σ₂₃] G) {C : ℝ≥0∞} + (hC : ∀ x y, ‖f x y‖ₑ ≤ C * ‖x‖ₑ * ‖y‖ₑ) : ‖f‖ₑ ≤ C := + f.opENorm_le_bound fun x => (f x).opENorm_le_bound <| hC x + +theorem le_opENorm₂ [RingHomIsometric σ₁₃] (f : E →SL[σ₁₃] F →SL[σ₂₃] G) (x : E) (y : F) : + ‖f x y‖ₑ ≤ ‖f‖ₑ * ‖x‖ₑ * ‖y‖ₑ := + (f x).le_of_opENorm_le (f.le_opENorm x) y end OpNorm @@ -165,6 +171,14 @@ theorem flip_flip (f : E →SL[σ₁₃] F →SL[σ₂₃] G) : f.flip.flip = f theorem opNorm_flip (f : E →SL[σ₁₃] F →SL[σ₂₃] G) : ‖f.flip‖ = ‖f‖ := le_antisymm (by simpa only [flip_flip] using le_norm_flip f.flip) (le_norm_flip f) +@[simp] +theorem opNNNorm_flip (f : E →SL[σ₁₃] F →SL[σ₂₃] G) : ‖f.flip‖₊ = ‖f‖₊ := by + simp [← NNReal.coe_inj] + +@[simp] +theorem opENorm_flip (f : E →SL[σ₁₃] F →SL[σ₂₃] G) : ‖f.flip‖ₑ = ‖f‖ₑ := by + simp [enorm_eq_nnnorm] + @[simp] lemma flip_zero : flip (0 : E →SL[σ₁₃] F →SL[σ₂₃] G) = 0 := rfl diff --git a/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean b/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean index 7a8e40734c48b4..9a8ca0e373bacc 100644 --- a/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean +++ b/Mathlib/Analysis/Normed/Operator/Compact/Basic.lean @@ -6,6 +6,7 @@ Authors: Anatole Dedecker module public import Mathlib.Analysis.LocallyConvex.Bounded +public import Mathlib.Tactic.CrossRefAttribute public import Mathlib.Topology.Algebra.Module.Spaces.ContinuousLinearMap /-! @@ -66,6 +67,7 @@ but we choose a definition which involves fewer existential quantifiers and repl with preimages. We prove the equivalence in `isCompactOperator_iff_exists_mem_nhds_image_subset_compact`. -/ +@[wikidata Q1780743] def IsCompactOperator {M₁ M₂ : Type*} [Zero M₁] [TopologicalSpace M₁] [TopologicalSpace M₂] (f : M₁ → M₂) : Prop := ∃ K, IsCompact K ∧ f ⁻¹' K ∈ (𝓝 0 : Filter M₁) diff --git a/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean b/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean index 9ab76683e106c2..bca3379c205e6f 100644 --- a/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean +++ b/Mathlib/Analysis/Normed/Operator/LinearIsometry.lean @@ -284,6 +284,9 @@ theorem diam_range : Metric.diam (range f) = Metric.diam (univ : Set E) := def toContinuousLinearMap : E →SL[σ₁₂] E₂ := ⟨f.toLinearMap, f.continuous⟩ +@[simp] lemma toLinearMap_toContinuousLinearMap (f : E →ₛₗᵢ[σ₁₂] E₂) : + f.toContinuousLinearMap.toLinearMap = f.toLinearMap := rfl + theorem toContinuousLinearMap_injective : Function.Injective (toContinuousLinearMap : _ → E →SL[σ₁₂] E₂) := fun x _ h => coe_injective (congr_arg _ h : ⇑x.toContinuousLinearMap = _) diff --git a/Mathlib/Analysis/Normed/Operator/Mul.lean b/Mathlib/Analysis/Normed/Operator/Mul.lean index 1ed482a0a80254..3cf11c69837973 100644 --- a/Mathlib/Analysis/Normed/Operator/Mul.lean +++ b/Mathlib/Analysis/Normed/Operator/Mul.lean @@ -221,6 +221,10 @@ theorem opNNNorm_lsmul_le : ‖(lsmul 𝕜 R : R →L[𝕜] E →L[𝕜] E)‖ rw [← NNReal.coe_le_coe] simpa using opNorm_lsmul_le +theorem opENorm_lsmul_le : ‖(lsmul 𝕜 R : R →L[𝕜] E →L[𝕜] E)‖ₑ ≤ 1 := by + rw [enorm_eq_nnnorm] + simpa using opNNNorm_lsmul_le + end SMulLinear end ContinuousLinearMap @@ -247,6 +251,10 @@ theorem opNorm_mul : ‖mul 𝕜 R‖ = 1 := theorem opNNNorm_mul : ‖mul 𝕜 R‖₊ = 1 := Subtype.ext <| opNorm_mul 𝕜 R +@[simp] +theorem opENorm_mul : ‖mul 𝕜 R‖ₑ = 1 := by + simp [enorm_eq_nnnorm] + end /-- The norm of `lsmul` equals 1 in any nontrivial normed group. @@ -268,6 +276,11 @@ theorem opNNNorm_lsmul [NormedDivisionRing R] [NormedAlgebra 𝕜 R] [Module R E rw [← NNReal.coe_inj] simp +@[simp] +theorem opENorm_lsmul [NormedDivisionRing R] [NormedAlgebra 𝕜 R] [Module R E] [NormSMulClass R E] + [IsScalarTower 𝕜 R E] [Nontrivial E] : ‖(lsmul 𝕜 R : R →L[𝕜] E →L[𝕜] E)‖ₑ = 1 := by + simp [enorm_eq_nnnorm] + /-- The norm of `lsmul x` equals `‖x‖` in any nontrivial normed group. This is `ContinuousLinearMap.opNorm_lsmul_apply_le` as an equality. -/ @@ -288,6 +301,12 @@ theorem opNNNorm_lsmul_apply [NormedDivisionRing R] [NormedAlgebra 𝕜 R] [Modu rw [← NNReal.coe_inj] simp +@[simp] +theorem opENorm_lsmul_apply [NormedDivisionRing R] [NormedAlgebra 𝕜 R] [Module R E] + [NormSMulClass R E] [IsScalarTower 𝕜 R E] [Nontrivial E] {a : R} : + ‖(lsmul 𝕜 R a : E →L[𝕜] E)‖ₑ = ‖a‖ₑ := by + simp [enorm_eq_nnnorm] + end ContinuousLinearMap end Normed diff --git a/Mathlib/Analysis/Normed/Operator/NNNorm.lean b/Mathlib/Analysis/Normed/Operator/NNNorm.lean index 0eff611679c554..f2ae9cbd2f6026 100644 --- a/Mathlib/Analysis/Normed/Operator/NNNorm.lean +++ b/Mathlib/Analysis/Normed/Operator/NNNorm.lean @@ -21,7 +21,7 @@ suppress_compilation open Bornology open Filter hiding map_smul -open scoped NNReal Topology Uniformity +open scoped NNReal Topology Uniformity ENNReal open Metric ContinuousLinearMap open Set Real @@ -88,6 +88,34 @@ theorem le_opNNNorm (f : E →SL[σ₁₂] F) (x : E) : ‖f x‖₊ ≤ ‖f‖ lemma le_opENorm (f : E →SL[σ₁₂] F) (x : E) : ‖f x‖ₑ ≤ ‖f‖ₑ * ‖x‖ₑ := by dsimp [enorm]; exact mod_cast le_opNNNorm .. +@[deprecated (since := "2026-06-27")] alias le_opNorm_enorm := le_opENorm + +/-- If one controls the enorm of every `f x`, then one controls the enorm of `f`. -/ +theorem opENorm_le_bound (f : E →SL[σ₁₂] F) {M : ℝ≥0∞} (hM : ∀ x, ‖f x‖ₑ ≤ M * ‖x‖ₑ) : + ‖f‖ₑ ≤ M := by + rcases eq_top_or_lt_top M with rfl | h'M + · simp + lift M to NNReal using h'M.ne + simp only [← ofReal_norm, ENNReal.ofReal_le_coe] + apply opNorm_le_bound _ (by positivity) (fun x ↦ ?_) + specialize hM x + simp only [← ofReal_norm, ← ENNReal.ofReal_coe_nnreal] at hM + rwa [← ENNReal.ofReal_mul (by positivity), ENNReal.ofReal_le_ofReal_iff (by positivity)] at hM + +theorem le_of_opENorm_le_of_le (f : E →SL[σ₁₂] F) {x} {a b : ℝ≥0∞} (hf : ‖f‖ₑ ≤ a) (hx : ‖x‖ₑ ≤ b) : + ‖f x‖ₑ ≤ a * b := + (f.le_opENorm x).trans <| by gcongr + +theorem le_opENorm_of_le (f : E →SL[σ₁₂] F) {c : ℝ≥0∞} {x} (h : ‖x‖ₑ ≤ c) : ‖f x‖ₑ ≤ ‖f‖ₑ * c := + f.le_of_opENorm_le_of_le le_rfl h + +theorem le_of_opENorm_le (f : E →SL[σ₁₂] F) {c : ℝ≥0∞} (h : ‖f‖ₑ ≤ c) (x : E) : ‖f x‖ₑ ≤ c * ‖x‖ₑ := + f.le_of_opENorm_le_of_le h le_rfl + +theorem opENorm_le_iff {f : E →SL[σ₁₂] F} {M : ℝ≥0∞} : + ‖f‖ₑ ≤ M ↔ ∀ x, ‖f x‖ₑ ≤ M * ‖x‖ₑ := + ⟨f.le_of_opENorm_le, opENorm_le_bound f⟩ + theorem nndist_le_opNNNorm (f : E →SL[σ₁₂] F) (x y : E) : nndist (f x) (f y) ≤ ‖f‖₊ * nndist x y := dist_le_opNorm f x y diff --git a/Mathlib/Analysis/Normed/Operator/NormedSpace.lean b/Mathlib/Analysis/Normed/Operator/NormedSpace.lean index 360f0ccdb8be29..74ba95d31cf2bd 100644 --- a/Mathlib/Analysis/Normed/Operator/NormedSpace.lean +++ b/Mathlib/Analysis/Normed/Operator/NormedSpace.lean @@ -438,7 +438,7 @@ lemma ContinuousLinearMap.norm_inl [SeminormedAddCommGroup E] [NontrivialTopolog ‖ContinuousLinearMap.inl 𝕜 E F‖ = 1 := (LinearIsometry.inl 𝕜 E F).norm_toContinuousLinearMap -lemma ContinuousLinearMap.norm_inr [SeminormedAddCommGroup E] [NontrivialTopology E] +lemma ContinuousLinearMap.norm_inr [SeminormedAddCommGroup E] [NormedSpace 𝕜 E] [SeminormedAddCommGroup F] [NormedSpace 𝕜 F] [NontrivialTopology F] : ‖ContinuousLinearMap.inr 𝕜 E F‖ = 1 := (LinearIsometry.inr 𝕜 E F).norm_toContinuousLinearMap diff --git a/Mathlib/Analysis/Normed/Ring/Basic.lean b/Mathlib/Analysis/Normed/Ring/Basic.lean index 377e845d613b2b..5ffd65f31cb975 100644 --- a/Mathlib/Analysis/Normed/Ring/Basic.lean +++ b/Mathlib/Analysis/Normed/Ring/Basic.lean @@ -43,6 +43,9 @@ class NonUnitalSeminormedRing (α : Type*) extends Norm α, NonUnitalRing α, /-- The norm is submultiplicative. -/ protected norm_mul_le : ∀ a b, norm (a * b) ≤ norm a * norm b +-- see Note [lower instance priority] +attribute [instance 10] NonUnitalSeminormedRing.toNonUnitalRing + /-- A seminormed ring is a ring endowed with a seminorm which satisfies the inequality `‖x y‖ ≤ ‖x‖ ‖y‖`. -/ class SeminormedRing (α : Type*) extends Norm α, Ring α, PseudoMetricSpace α where @@ -51,6 +54,9 @@ class SeminormedRing (α : Type*) extends Norm α, Ring α, PseudoMetricSpace α /-- The norm is submultiplicative. -/ norm_mul_le : ∀ a b, norm (a * b) ≤ norm a * norm b +-- see Note [lower instance priority] +attribute [instance 10] SeminormedRing.toRing + -- see Note [lower instance priority] /-- A seminormed ring is a non-unital seminormed ring. -/ instance (priority := 100) SeminormedRing.toNonUnitalSeminormedRing [β : SeminormedRing α] : @@ -65,6 +71,9 @@ class NonUnitalNormedRing (α : Type*) extends Norm α, NonUnitalRing α, Metric /-- The norm is submultiplicative. -/ norm_mul_le : ∀ a b, norm (a * b) ≤ norm a * norm b +-- see Note [lower instance priority] +attribute [instance 10] NonUnitalNormedRing.toNonUnitalRing + -- see Note [lower instance priority] /-- A non-unital normed ring is a non-unital seminormed ring. -/ instance (priority := 100) NonUnitalNormedRing.toNonUnitalSeminormedRing @@ -78,6 +87,9 @@ class NormedRing (α : Type*) extends Norm α, Ring α, MetricSpace α where /-- The norm is submultiplicative. -/ norm_mul_le : ∀ a b, norm (a * b) ≤ norm a * norm b +-- see Note [lower instance priority] +attribute [instance 10] NormedRing.toRing + -- see Note [lower instance priority] /-- A normed ring is a seminormed ring. -/ instance (priority := 100) NormedRing.toSeminormedRing [β : NormedRing α] : SeminormedRing α := @@ -95,14 +107,14 @@ class NonUnitalSeminormedCommRing (α : Type*) extends NonUnitalSeminormedRing α, NonUnitalCommRing α where -- see Note [lower instance priority] -attribute [instance 100] NonUnitalSeminormedCommRing.toNonUnitalCommRing +attribute [instance 10] NonUnitalSeminormedCommRing.toNonUnitalCommRing /-- A non-unital normed commutative ring is a non-unital commutative ring endowed with a norm which satisfies the inequality `‖x y‖ ≤ ‖x‖ ‖y‖`. -/ class NonUnitalNormedCommRing (α : Type*) extends NonUnitalNormedRing α, NonUnitalCommRing α where -- see Note [lower instance priority] -attribute [instance 100] NonUnitalNormedCommRing.toNonUnitalCommRing +attribute [instance 10] NonUnitalNormedCommRing.toNonUnitalCommRing -- see Note [lower instance priority] /-- A non-unital normed commutative ring is a non-unital seminormed commutative ring. -/ @@ -115,14 +127,14 @@ the inequality `‖x y‖ ≤ ‖x‖ ‖y‖`. -/ class SeminormedCommRing (α : Type*) extends SeminormedRing α, CommRing α where -- see Note [lower instance priority] -attribute [instance 100] SeminormedCommRing.toCommRing +attribute [instance 10] SeminormedCommRing.toCommRing /-- A normed commutative ring is a commutative ring endowed with a norm which satisfies the inequality `‖x y‖ ≤ ‖x‖ ‖y‖`. -/ class NormedCommRing (α : Type*) extends NormedRing α, CommRing α where -- see Note [lower instance priority] -attribute [instance 100] NormedCommRing.toCommRing +attribute [instance 10] NormedCommRing.toCommRing -- see Note [lower instance priority] /-- A seminormed commutative ring is a non-unital seminormed commutative ring. -/ diff --git a/Mathlib/Analysis/Normed/Unbundled/AlgebraNorm.lean b/Mathlib/Analysis/Normed/Unbundled/AlgebraNorm.lean index 607ec63d94e17e..55997cb1fd7839 100644 --- a/Mathlib/Analysis/Normed/Unbundled/AlgebraNorm.lean +++ b/Mathlib/Analysis/Normed/Unbundled/AlgebraNorm.lean @@ -211,15 +211,11 @@ namespace MulRingNorm variable {R : Type*} [NonAssocRing R] -set_option linter.style.whitespace false in -- manual alignment is not recognised /-- The ring norm underlying a multiplicative ring norm. -/ def toRingNorm (f : MulRingNorm R) : RingNorm R where - toFun := f - map_zero' := f.map_zero' - add_le' := f.add_le' - neg' := f.neg' + toFun := f + __ := f mul_le' x y := le_of_eq (f.map_mul' x y) - eq_zero_of_map_eq_zero' := f.eq_zero_of_map_eq_zero' /-- A multiplicative ring norm is power-multiplicative. -/ theorem isPowMul {A : Type*} [Ring A] (f : MulRingNorm A) : IsPowMul f := fun x n hn => by diff --git a/Mathlib/Analysis/Normed/Unbundled/SeminormFromBounded.lean b/Mathlib/Analysis/Normed/Unbundled/SeminormFromBounded.lean index f9928117449d40..2c06c768cb8bc9 100644 --- a/Mathlib/Analysis/Normed/Unbundled/SeminormFromBounded.lean +++ b/Mathlib/Analysis/Normed/Unbundled/SeminormFromBounded.lean @@ -9,6 +9,7 @@ public import Mathlib.Analysis.Normed.Unbundled.RingSeminorm /-! # seminormFromBounded + In this file, we prove [BGR, Proposition 1.2.1/2][bosch-guntzer-remmert] : given a nonzero additive group seminorm on a commutative ring `R` such that for some `c : ℝ` and every `x y : R`, the inequality `f (x * y) ≤ c * f x * f y)` is satisfied, we create a ring seminorm on `R`. diff --git a/Mathlib/Analysis/Normed/Unbundled/SmoothingSeminorm.lean b/Mathlib/Analysis/Normed/Unbundled/SmoothingSeminorm.lean index b6c8e7970aadac..9e515e621bb23f 100644 --- a/Mathlib/Analysis/Normed/Unbundled/SmoothingSeminorm.lean +++ b/Mathlib/Analysis/Normed/Unbundled/SmoothingSeminorm.lean @@ -14,6 +14,7 @@ public import Mathlib.Topology.Algebra.Order.LiminfLimsup /-! # smoothingSeminorm + In this file, we prove [BGR, Proposition 1.3.2/1][bosch-guntzer-remmert]: if `μ` is a nonarchimedean seminorm on a commutative ring `R`, then `iInf (fun (n : PNat), (μ(x ^ (n : ℕ))) ^ (1 / (n : ℝ)))` is a power-multiplicative nonarchimedean diff --git a/Mathlib/Analysis/ODE/Gronwall.lean b/Mathlib/Analysis/ODE/Gronwall.lean index 9529632c1d932e..bedc58a9ea540b 100644 --- a/Mathlib/Analysis/ODE/Gronwall.lean +++ b/Mathlib/Analysis/ODE/Gronwall.lean @@ -174,7 +174,7 @@ theorem dist_le_of_approx_trajectories_ODE_of_mem simp only [dist_eq_norm] at ha ⊢ have h_deriv : ∀ t ∈ Ico a b, HasDerivWithinAt (fun t => f t - g t) (f' t - g' t) (Ici t) t := fun t ht => (hf' t ht).sub (hg' t ht) - apply norm_le_gronwallBound_of_norm_deriv_right_le (hf.sub hg) h_deriv ha + apply norm_le_gronwallBound_of_norm_deriv_right_le (hf.fun_sub hg) h_deriv ha intro t ht have := dist_triangle4_right (f' t) (g' t) (v t (f t)) (v t (g t)) have := (hv t ht).dist_le_mul _ (hfs t ht) _ (hgs t ht) diff --git a/Mathlib/Analysis/ODE/Transform.lean b/Mathlib/Analysis/ODE/Transform.lean index 1038e089811893..9f7120d18118ff 100644 --- a/Mathlib/Analysis/ODE/Transform.lean +++ b/Mathlib/Analysis/ODE/Transform.lean @@ -72,7 +72,7 @@ lemma IsIntegralCurveAt.comp_add (hγ : IsIntegralCurveAt γ v t₀) (dt : ℝ) isIntegralCurveAt_comp_add.mpr hγ lemma isIntegralCurveAt_comp_sub {dt : ℝ} : - IsIntegralCurveAt (γ ∘ (· - dt)) (v ∘ (· - dt)) (t₀ + dt) ↔ IsIntegralCurveAt γ v t₀ := by + IsIntegralCurveAt (γ ∘ (· - dt)) (v ∘ (· - dt)) (t₀ + dt) ↔ IsIntegralCurveAt γ v t₀ := by simpa using! isIntegralCurveAt_comp_add (dt := -dt) lemma IsIntegralCurveAt.comp_sub (hγ : IsIntegralCurveAt γ v t₀) (dt : ℝ) : diff --git a/Mathlib/Analysis/Polynomial/MahlerMeasure.lean b/Mathlib/Analysis/Polynomial/MahlerMeasure.lean index f3abb35d18507e..71d466e6a1089c 100644 --- a/Mathlib/Analysis/Polynomial/MahlerMeasure.lean +++ b/Mathlib/Analysis/Polynomial/MahlerMeasure.lean @@ -293,7 +293,6 @@ theorem mahlerMeasure_le_sum_norm_coeff (p : ℂ[X]) : p.mahlerMeasure ≤ p.sum apply norm_sum_le_of_le p.support simp -set_option linter.style.emptyLine false in open MeasureTheory Set in /-- **Landau's inequality**: the Mahler measure of a polynomial is at most the ℓ² norm of its coefficient vector, `√(∑ ‖coeff i‖²)`. diff --git a/Mathlib/Analysis/RCLike/Extend.lean b/Mathlib/Analysis/RCLike/Extend.lean index 9bfd56a416bf36..f1edf68502006f 100644 --- a/Mathlib/Analysis/RCLike/Extend.lean +++ b/Mathlib/Analysis/RCLike/Extend.lean @@ -103,9 +103,6 @@ variable [Module ℝ F] [IsScalarTower ℝ 𝕜 F] /-- Extend `fr : StrongDual ℝ F` to `StrongDual 𝕜 F`. -It would be possible to use `LinearMap.mkContinuous` here, but we would need to know that the -continuity of `fr` implies it has bounded norm and we want to avoid that dependency here. - Norm properties of this extension can be found in `Mathlib/Analysis/Normed/Module/RCLike/Extend.lean`. -/ noncomputable def extendRCLike (fr : StrongDual ℝ F) : StrongDual 𝕜 F where diff --git a/Mathlib/Analysis/Real/Sqrt.lean b/Mathlib/Analysis/Real/Sqrt.lean index bb2d0f3c36425a..ebd06fcb895a4b 100644 --- a/Mathlib/Analysis/Real/Sqrt.lean +++ b/Mathlib/Analysis/Real/Sqrt.lean @@ -303,6 +303,18 @@ lemma sqrt_le_sqrt_iff' (hx : 0 < x) : √x ≤ √y ↔ x ≤ y := by @[simp] lemma isSquare_iff : IsSquare x ↔ 0 ≤ x := ⟨(·.nonneg), (⟨√x, mul_self_sqrt · |>.symm⟩)⟩ +@[simp] lemma sqrt_le_self_iff : √x ≤ x ↔ x = 0 ∨ 1 ≤ x := by + rw [sqrt_le_iff, ← sub_nonneg (a := x ^ 2), sq, ← mul_sub_one] + grind [mul_nonneg_iff] + +@[simp] lemma le_sqrt_self_iff : x ≤ √x ↔ x ≤ 1 := by + obtain hx | hx := le_or_gt x 0 + · simp [hx.trans] + · rw [le_sqrt' hx, sq, mul_le_iff_le_one_left hx] + +@[simp] lemma sqrt_lt_self_iff : √x < x ↔ 1 < x := by simp [← not_le] +@[simp] lemma lt_sqrt_self_iff : x < √x ↔ x ≠ 0 ∧ x < 1 := by simp [← not_le] + end Real namespace Mathlib.Meta.Positivity @@ -312,8 +324,8 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: a square root of a strictly positive nonnegative real is positive. -/ @[positivity NNReal.sqrt _] -meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(NNReal), ~q(NNReal.sqrt $a) => assertInstancesCommute @@ -326,8 +338,8 @@ meta def evalNNRealSqrt : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for the `positivity` tactic: a square root is nonnegative, and is strictly positive if its input is. -/ @[positivity √_] -meta def evalSqrt : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalSqrt : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(√$a) => assertInstancesCommute diff --git a/Mathlib/Analysis/Seminorm.lean b/Mathlib/Analysis/Seminorm.lean index 5cb671c016ff74..7e79c81ce21402 100644 --- a/Mathlib/Analysis/Seminorm.lean +++ b/Mathlib/Analysis/Seminorm.lean @@ -1175,7 +1175,7 @@ theorem continuous_of_le [TopologicalSpace E] [IsTopologicalAddGroup E] exact isOpen_lt hq continuous_const /-- The sum over a finite set of continuous seminorms is continuous. -/ -theorem continuous_finsetSum [TopologicalSpace E] [IsTopologicalAddGroup E] +theorem continuous_finsetSum [TopologicalSpace E] {p : ι → Seminorm 𝕝 E} {s : Finset ι} (hp : ∀ i ∈ s, Continuous (p i)) : Continuous ((∑ i ∈ s, p i : Seminorm 𝕝 E) : E → ℝ) := by change Continuous (fun x ↦ coeFnAddMonoidHom _ _ (∑ i ∈ s, p i) x) diff --git a/Mathlib/Analysis/SpecialFunctions/Bernstein.lean b/Mathlib/Analysis/SpecialFunctions/Bernstein.lean index 2e650b4aa8f761..b6bbf6d738c11c 100644 --- a/Mathlib/Analysis/SpecialFunctions/Bernstein.lean +++ b/Mathlib/Analysis/SpecialFunctions/Bernstein.lean @@ -80,9 +80,9 @@ open Lean Meta Qq Function /-- Extension of the `positivity` tactic for Bernstein polynomials: they are always non-negative. -/ @[positivity DFunLike.coe (bernstein _ _) _] -meta def evalBernstein : PositivityExt where eval {_ _} _zα pα? e := do +meta def evalBernstein : PositivityExt where eval {_ _} _zα pα? e := + match pα? with | none => pure .none | some _ => do let .app (.app _coe (.app (.app _ n) ν)) x ← whnfR e | throwError "not bernstein polynomial" - let some _ := pα? | pure .none let p ← mkAppOptM ``bernstein_nonneg #[n, ν, x] pure (.nonnegative p) @@ -193,7 +193,7 @@ theorem bernsteinApproximation_uniform [LocallyConvexSpace ℝ E] (f : C(I, E)) |>.compactConvergenceUniformity_of_compact |> nhds_basis_uniformity |>.tendsto_right_iff] rintro U ⟨hU₀, hcU⟩ filter_upwards [this U hU₀ hcU] with n hn x - exact gauge_lt_one_subset_self hcU (mem_of_mem_nhds hU₀) (absorbent_nhds_zero hU₀) (hn x) + exact setOf_gauge_lt_one_subset_self hcU (mem_of_mem_nhds hU₀) (absorbent_nhds_zero hU₀) (hn x) intro U hU₀ hUc /- Choose a constant `C` such that `‖f x - f y‖_U ≤ C` for all `x`, `y`. For a normed space, this would be twice the norm of `f`. -/ diff --git a/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean b/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean index f4a81b97557852..2af2e998881d35 100644 --- a/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean +++ b/Mathlib/Analysis/SpecialFunctions/ContinuousFunctionalCalculus/PosPart/Basic.lean @@ -148,6 +148,9 @@ lemma negPart_nonneg (a : A) : 0 ≤ a⁻ := cfcₙ_nonneg (fun x _ ↦ by positivity) +instance : SelfAdjointDecompose A where + exists_nonneg_sub_nonneg {a} ha := ⟨a⁺, a⁻, by cfc_tac, by cfc_tac, (posPart_sub_negPart a).symm⟩ + lemma posPart_eq_of_eq_sub_negPart {a b : A} (hab : a = b - a⁻) (hb : 0 ≤ b := by cfc_tac) : a⁺ = b := by have ha := hab.symm ▸ hb.isSelfAdjoint.sub (negPart_nonneg a).isSelfAdjoint diff --git a/Mathlib/Analysis/SpecialFunctions/Exponential.lean b/Mathlib/Analysis/SpecialFunctions/Exponential.lean index d4285ca6039335..fa82b301845597 100644 --- a/Mathlib/Analysis/SpecialFunctions/Exponential.lean +++ b/Mathlib/Analysis/SpecialFunctions/Exponential.lean @@ -279,8 +279,8 @@ theorem hasFDerivAt_exp_smul_const_of_mem_ball (x : 𝔸) (t : 𝕊) have : Commute (t • x) (h • x) := ((Commute.refl x).smul_left t).smul_right h rw [add_smul t h, exp_add_of_commute_of_mem_ball this htx hh, zero_add, zero_smul, exp_zero, ContinuousLinearMap.smulRight_apply, one_apply_eq_self, - _root_.smul_apply, ContinuousLinearMap.smulRight_apply, - one_apply_eq_self, smul_eq_mul, mul_sub_left_distrib, mul_sub_left_distrib, mul_one] + smul_apply, ContinuousLinearMap.smulRight_apply, one_apply_eq_self, smul_eq_mul, + mul_sub_left_distrib, mul_sub_left_distrib, mul_one] theorem hasFDerivAt_exp_smul_const_of_mem_ball' (x : 𝔸) (t : 𝕊) (htx : t • x ∈ Metric.eball (0 : 𝔸) (expSeries 𝕂 𝔸).radius) : diff --git a/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean index 16c34c92f0cf0a..a57539a4f3e2de 100644 --- a/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean +++ b/Mathlib/Analysis/SpecialFunctions/Gamma/Basic.lean @@ -472,10 +472,10 @@ lemma integral_rpow_mul_exp_neg_mul_Ioi {a r : ℝ} (ha : 0 < a) (hr : 0 < r) : open Lean.Meta Qq Mathlib.Meta.Positivity in /-- The `positivity` extension which identifies expressions of the form `Gamma a`. -/ @[positivity Gamma (_ : ℝ)] -meta def _root_.Mathlib.Meta.Positivity.evalGamma : PositivityExt where eval {u α} _zα pα? e := do +meta def _root_.Mathlib.Meta.Positivity.evalGamma : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Gamma $a) => - let some _ := pα? | pure .none match ← core q(inferInstance) (some q(inferInstance)) a with | .positive pa => assertInstancesCommute diff --git a/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean index 0dc992510e3b42..e77c76e334d3e1 100644 --- a/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean +++ b/Mathlib/Analysis/SpecialFunctions/Integrability/Basic.lean @@ -187,9 +187,6 @@ theorem integrableOn_Ioo_cpow_iff {s : ℂ} {t : ℝ} (ht : 0 < t) : theorem intervalIntegrable_id : IntervalIntegrable (fun x => x) μ a b := continuous_id.intervalIntegrable a b -theorem intervalIntegrable_const : IntervalIntegrable (fun _ => c) μ a b := - continuous_const.intervalIntegrable a b - theorem intervalIntegrable_one_div (h : ∀ x : ℝ, x ∈ [[a, b]] → f x ≠ 0) (hf : ContinuousOn f [[a, b]]) : IntervalIntegrable (fun x => 1 / f x) μ a b := (continuousOn_const.div hf h).intervalIntegrable diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean index a50cc14b759129..cc7333c231aa1b 100644 --- a/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean +++ b/Mathlib/Analysis/SpecialFunctions/Log/Basic.lean @@ -596,30 +596,30 @@ lemma log_nz_of_isRat_neg {n : ℤ} : (NormNum.IsRat e n d) → (decide (n / d < /-- Extension for the `positivity` tactic: `Real.log` of a natural number is always nonnegative. -/ @[positivity Real.log (Nat.cast _)] -meta def evalLogNatCast : PositivityExt where eval {u α} _zα pα? e := do +meta def evalLogNatCast : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Real.log (Nat.cast $a)) => - let some _ := pα? | pure .none assertInstancesCommute pure (.nonnegative q(Real.log_natCast_nonneg $a)) | _, _, _ => throwError "not Real.log" /-- Extension for the `positivity` tactic: `Real.log` of an integer is always nonnegative. -/ @[positivity Real.log (Int.cast _)] -meta def evalLogIntCast : PositivityExt where eval {u α} _zα pα? e := do +meta def evalLogIntCast : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Real.log (Int.cast $a)) => - let some _ := pα? | pure .none assertInstancesCommute pure (.nonnegative q(Real.log_intCast_nonneg $a)) | _, _, _ => throwError "not Real.log" /-- Extension for the `positivity` tactic: `Real.log` of a numeric literal. -/ @[positivity Real.log _] -meta def evalLogNatLit : PositivityExt where eval {u α} _ pα? e := do +meta def evalLogNatLit : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Real.log $a) => - let some _ := pα? | pure .none match ← NormNum.derive a with | .isNat (_ : Q(AddMonoidWithOne ℝ)) lit p => assumeInstancesCommute diff --git a/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean b/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean index 99ebf667e962ce..d41254b9cd1ef3 100644 --- a/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean +++ b/Mathlib/Analysis/SpecialFunctions/Log/Deriv.lean @@ -253,8 +253,6 @@ theorem abs_log_sub_add_sum_range_le {x : ℝ} (h : |x| < 1) (n : ℕ) : -- fourth step: conclude by massaging the inequality of the third step simpa [F, div_mul_eq_mul_div, pow_succ] using C --- see https://github.com/leanprover-community/mathlib4/issues/29041 -set_option linter.unusedSimpArgs false in /-- Compute the derivative of the difference between $\frac{1}{2} * \log(\frac{1+x}{1-x})$ and its Taylor series at `0` up to order `n`. This is an auxiliary lemma for @@ -270,14 +268,14 @@ lemma hasDerivAt_half_log_one_add_div_one_sub_sub_sum_range refine ((((((hasDerivAt_id _).const_add _).div ((hasDerivAt_id _).const_sub _) (by grind)).log ?_).const_mul _).sub (HasDerivAt.fun_sum fun i hi ↦ (hasDerivAt_pow _ _).div_const _)) |>.congr_deriv ?_ - · simp only [id_eq, div_ne_zero_iff, Pi.div_apply]; grind + · simp only [div_ne_zero_iff, Pi.div_apply]; grind have : (∑ i ∈ range n, (2 * i + 1) * y ^ (2 * i) / (2 * i + 1)) = (∑ i ∈ range n, (y ^ 2) ^ i) := by congr with i simp [field, mul_comm, ← pow_mul] have hy₃ : y ^ 2 ≠ 1 := by simp [hy₁.ne', hy₂.ne] have hy₄ : (1 - y) * (1 + y) = 1 - y ^ 2 := by ring - simp [this, field, geom_sum_eq hy₃, hy₄, sub_ne_zero_of_ne, hy₃.symm] + simp [this, field, geom_sum_eq hy₃, hy₄] ring /-- A lemma estimating the difference between $\frac{1}{2} * \log(\frac{1+x}{1-x})$ and its diff --git a/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean b/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean index 72625a7781eea3..913cb41fbbf83a 100644 --- a/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean +++ b/Mathlib/Analysis/SpecialFunctions/Log/NegMulLog.lean @@ -184,7 +184,7 @@ lemma negMulLog_mul (x y : ℝ) : negMulLog (x * y) = y * negMulLog x + x * negM ring @[fun_prop] lemma continuous_negMulLog : Continuous negMulLog := by - simpa only [negMulLog_eq_neg] using continuous_mul_log.neg + simpa only [negMulLog_eq_neg] using continuous_mul_log.fun_neg lemma differentiableOn_negMulLog : DifferentiableOn ℝ negMulLog {0}ᶜ := by simpa only [negMulLog_eq_neg] using! differentiableOn_mul_log.neg diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean b/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean index ccde81c19c3eca..6e98036e4a5139 100644 --- a/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean +++ b/Mathlib/Analysis/SpecialFunctions/Pow/NNReal.lean @@ -1125,8 +1125,8 @@ open Lean Meta Qq the base is nonnegative and positive when the base is positive. This is the `NNReal` analogue of `evalRpow` for `Real`. -/ @[positivity (_ : ℝ≥0) ^ (_ : ℝ)] -meta def evalNNRealRpow : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | pure .none +meta def evalNNRealRpow : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ≥0), ~q($a ^ (0 : ℝ)) => assertInstancesCommute @@ -1155,8 +1155,8 @@ private meta def isFiniteM? (x : Q(ℝ≥0∞)) : MetaM (Option Q($x ≠ (⊤ : the base is nonnegative and positive when the base is positive. This is the `ENNReal` analogue of `evalRpow` for `Real`. -/ @[positivity (_ : ℝ≥0∞) ^ (_ : ℝ)] -meta def evalENNRealRpow : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | pure .none +meta def evalENNRealRpow : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ≥0∞), ~q($a ^ (0 : ℝ)) => assertInstancesCommute diff --git a/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean b/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean index b220c5e0e31fdb..4fa7722f332deb 100644 --- a/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean +++ b/Mathlib/Analysis/SpecialFunctions/Pow/Real.lean @@ -375,8 +375,8 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: exponentiation by a real number is positive (namely 1) when the exponent is zero. The other cases are done in `evalRpow`. -/ @[positivity (_ : ℝ) ^ (0 : ℝ)] -meta def evalRpowZero : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | pure .none +meta def evalRpowZero : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q($a ^ (0 : ℝ)) => assertInstancesCommute @@ -386,8 +386,8 @@ meta def evalRpowZero : PositivityExt where eval {u α} _ pα? e := do /-- Extension for the `positivity` tactic: exponentiation by a real number is nonnegative when the base is nonnegative and positive when the base is positive. -/ @[positivity (_ : ℝ) ^ (_ : ℝ)] -meta def evalRpow : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalRpow : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q($a ^ ($b : ℝ)) => assertInstancesCommute diff --git a/Mathlib/Analysis/SpecialFunctions/Sigmoid.lean b/Mathlib/Analysis/SpecialFunctions/Sigmoid.lean index 2fb877b7f71693..81e3ca5316d90c 100644 --- a/Mathlib/Analysis/SpecialFunctions/Sigmoid.lean +++ b/Mathlib/Analysis/SpecialFunctions/Sigmoid.lean @@ -189,7 +189,7 @@ lemma ContDiff.sigmoid (hf : ContDiff ℝ ω f) : ContDiff ℝ ω (sigmoid ∘ f @[fun_prop] lemma differentiable_sigmoid : Differentiable ℝ sigmoid := - contDiff_sigmoid.of_le le_top |>.differentiable_one + contDiff_sigmoid.of_le le_top |>.differentiable_one @[fun_prop] lemma Differentiable.sigmoid (hf : Differentiable ℝ f) : Differentiable ℝ (sigmoid ∘ f) := diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean index db73145896a607..a4f209c1ff56de 100644 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean +++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Arctan.lean @@ -399,7 +399,8 @@ open Lean Meta Qq /-- Extension for `Real.arctan`. -/ @[positivity Real.arctan _] -meta def evalRealArctan : PositivityExt where eval {u α} z p e := do +meta def evalRealArctan : PositivityExt where eval {u α} z p e := + match p with | none => pure .none | some p => do match u, α, e with | 0, ~q(ℝ), ~q(Real.arctan $a) => let ra ← core z p a @@ -418,17 +419,18 @@ meta def evalRealArctan : PositivityExt where eval {u α} z p e := do /-- Extension for `Real.cos (Real.arctan _)`. -/ @[positivity Real.cos (Real.arctan _)] -meta def evalRealCosArctan : PositivityExt where eval {u α} _ pα? e := do +meta def evalRealCosArctan : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Real.cos (Real.arctan $a)) => - let some _ := pα? | pure .none assumeInstancesCommute return .positive q(Real.cos_arctan_pos _) | _ => throwError "not Real.cos (Real.arctan _)" /-- Extension for `Real.sin (Real.arctan _)`. -/ @[positivity Real.sin (Real.arctan _)] -meta def evalRealSinArctan : PositivityExt where eval {u α} z p e := do +meta def evalRealSinArctan : PositivityExt where eval {u α} z p e := + match p with | none => pure .none | some p => do match u, α, e with | 0, ~q(ℝ), ~q(Real.sin (Real.arctan $a)) => match ← core z p a with diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean index 6d712d0f1aa57e..86ee5817a80fec 100644 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean +++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Basic.lean @@ -177,10 +177,10 @@ open Lean.Meta Qq /-- Extension for the `positivity` tactic: `π` is always positive. -/ @[positivity Real.pi] -meta def evalRealPi : PositivityExt where eval {u α} _zα pα? e := do +meta def evalRealPi : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(Real.pi) => - let some _ := pα? | pure .none assertInstancesCommute pure (.positive q(Real.pi_pos)) | _, _, _ => throwError "not Real.pi" diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Chebyshev.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Chebyshev.lean deleted file mode 100644 index cce68ab67fd702..00000000000000 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Chebyshev.lean +++ /dev/null @@ -1,5 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Chebyshev.Basic - -deprecated_module (since := "2025-12-15") diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean index 46321ea1bf82e3..d5b7ed3e60971b 100644 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean +++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Cotangent.lean @@ -247,7 +247,7 @@ open scoped Nat variable (k : ℕ) private lemma contDiffOn_inv_linear (d : ℤ) : ContDiffOn ℂ k (fun z : ℂ ↦ 1 / (z + d)) ℂ_ℤ := by - simpa using ContDiffOn.inv (by fun_prop) (fun x hx ↦ integerComplement_add_ne_zero hx d) + simpa using ContDiffOn.fun_inv (by fun_prop) (fun x hx ↦ integerComplement_add_ne_zero hx d) lemma eqOn_iteratedDeriv_cotTerm (d : ℕ) : EqOn (iteratedDeriv k (fun z ↦ cotTerm z d)) diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean index d4ae1114b1700b..7568fe3549d181 100644 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean +++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/DerivHyp.lean @@ -798,8 +798,8 @@ alias ⟨_, sinh_ne_zero_of_ne_zero⟩ := Real.sinh_ne_zero /-- Extension for the `positivity` tactic: `Real.sinh` is positive/nonnegative/nonzero if its input is. -/ @[positivity Real.sinh _] -meta def evalSinh : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | pure .none +meta def evalSinh : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do let zα : Q(Zero ℝ) := q(inferInstance) let pα : Q(PartialOrder ℝ) := q(inferInstance) match u, α, e with diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean index 6597cad5fe44f4..a4a808bed3e34d 100644 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean +++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/EulerSineProd.lean @@ -160,8 +160,10 @@ theorem integral_cos_pow_eq (n : ℕ) : (∫ x in (0 : ℝ)..π / 2, cos x ^ n) = 1 / 2 * ∫ x in (0 : ℝ)..π, sin x ^ n := by rw [mul_comm (1 / 2 : ℝ), ← div_eq_iff (one_div_ne_zero (two_ne_zero' ℝ)), ← div_mul, div_one, mul_two] - have L : IntervalIntegrable _ volume 0 (π / 2) := (continuous_sin.pow n).intervalIntegrable _ _ - have R : IntervalIntegrable _ volume (π / 2) π := (continuous_sin.pow n).intervalIntegrable _ _ + have L : IntervalIntegrable _ volume 0 (π / 2) := + (continuous_sin.fun_pow n).intervalIntegrable _ _ + have R : IntervalIntegrable _ volume (π / 2) π := + (continuous_sin.fun_pow n).intervalIntegrable _ _ rw [← integral_add_adjacent_intervals L R] congr 1 · nth_rw 1 [(by ring : 0 = π / 2 - π / 2)] diff --git a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Inverse.lean b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Inverse.lean index 0ce590a12f66f8..806794b8a9289b 100644 --- a/Mathlib/Analysis/SpecialFunctions/Trigonometric/Inverse.lean +++ b/Mathlib/Analysis/SpecialFunctions/Trigonometric/Inverse.lean @@ -40,7 +40,8 @@ set_option backward.isDefEq.respectTransparency false in @[simp] theorem range_arcsin : range arcsin = Icc (-(π / 2)) (π / 2) := by rw [arcsin, range_comp Subtype.val] - simp [Icc] + ext + simp theorem arcsin_le_pi_div_two (x : ℝ) : arcsin x ≤ π / 2 := (arcsin_mem_Icc x).2 diff --git a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean index 86d80f40428592..47521a2a24aab7 100644 --- a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean +++ b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Colim.lean @@ -109,7 +109,6 @@ short complex `c₁.pt ⟶ c₂.pt ⟶ c₃.pt`. -/ @[simps] def colim.mapShortComplex : ShortComplex C := ShortComplex.mk f g (hc₁.hom_ext (fun j ↦ by - dsimp rw [reassoc_of% (hf j), hg j, comp_zero, ← NatTrans.comp_app_assoc, S.zero, zero_app, zero_comp])) diff --git a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean index 80c296ebe1f07c..2d124b5e493a0d 100644 --- a/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean +++ b/Mathlib/CategoryTheory/Abelian/GrothendieckAxioms/Connected.lean @@ -51,6 +51,7 @@ noncomputable def IsColimit.pullbackOfHasExactColimitsOfShape [HasPullbacks C] have := hc.isIso_colimMap_ι apply hpull.isIso_snd_of_isIso +set_option backward.isDefEq.respectTransparency false in /-- Detecting equality of morphisms factoring through a connected colimit by pulling back along the inclusions of the colimit. -/ theorem IsColimit.pullback_hom_ext [HasPullbacks C] [HasColimitsOfShape J C] diff --git a/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean b/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean index f92126f9c75b86..6c7b58e5c62803 100644 --- a/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean +++ b/Mathlib/CategoryTheory/Abelian/SerreClass/Localization.lean @@ -5,10 +5,9 @@ Authors: Joël Riou -/ module +public import Mathlib.Algebra.Homology.ShortComplex.ExactFunctor public import Mathlib.CategoryTheory.Abelian.SerreClass.MorphismProperty public import Mathlib.CategoryTheory.Localization.CalculusOfFractions.Preadditive -public import Mathlib.Algebra.Homology.ShortComplex.ExactFunctor -public import Mathlib.CategoryTheory.Limits.ExactFunctor /-! # Localization with respect to a Serre class @@ -29,7 +28,7 @@ universe v'' v' v u'' u' u namespace CategoryTheory -open Limits ZeroObject +open Limits namespace ObjectProperty @@ -139,9 +138,6 @@ lemma isZero_obj_iff (X : C) : rintro ⟨Y, h⟩ simpa using h.2 -lemma hasZeroObject : HasZeroObject D := - ⟨L.obj 0, by simpa [isZero_obj_iff L P] using P.prop_zero⟩ - lemma map_eq_zero_iff {X Y : C} (f : X ⟶ Y) : L.map f = 0 ↔ P (Abelian.image f) := by rw [← L.map_zero, MorphismProperty.map_eq_iff_precomp L P.isoModSerre] @@ -381,17 +377,9 @@ lemma hasCoequalizers : HasCoequalizers D := Preadditive.hasCoequalizer_of_hasCokernel _ _ hasCoequalizers_of_hasColimit_parallelPair _ -lemma hasBinaryProducts : HasBinaryProducts D := - have := Localization.essSurj L P.isoModSerre - have (X Y : D) : HasBinaryProduct X Y := - hasLimit_of_iso (show Limits.pair _ _ ≅ _ from - mapPairIso (L.objObjPreimageIso X) (L.objObjPreimageIso Y)) - hasBinaryProducts_of_hasLimit_pair D - lemma hasFiniteProducts : HasFiniteProducts D := - have := hasZeroObject L P - have := hasBinaryProducts L P - hasFiniteProducts_of_has_binary_and_terminal + have := Localization.essSurj L P.isoModSerre + L.hasFiniteProducts_of_additive_of_essSurj lemma isNormalMonoCategory : IsNormalMonoCategory D where normalMonoOfMono f hf := by @@ -438,6 +426,10 @@ def abelian : Abelian D := by have := isNormalEpiCategory L P constructor +lemma hasZeroObject : HasZeroObject D := + have := abelian L P + Abelian.hasZeroObject + lemma preservesFiniteLimits : PreservesFiniteLimits L := by letI := abelian L P rw [((Functor.preservesFiniteLimits_tfae L).out 3 2 :)] diff --git a/Mathlib/CategoryTheory/Adjunction/Basic.lean b/Mathlib/CategoryTheory/Adjunction/Basic.lean index ebe5a05c0cbd52..4ad3606f6184e9 100644 --- a/Mathlib/CategoryTheory/Adjunction/Basic.lean +++ b/Mathlib/CategoryTheory/Adjunction/Basic.lean @@ -575,12 +575,12 @@ def compUliftCoyonedaIso (adj : F ⊣ G) : section -variable {E : Type u₃} [ℰ : Category.{v₃} E] {H : D ⥤ E} {I : E ⥤ D} +variable {E : Type u₃} [Category.{v₃} E] {F : C ⥤ D} {G : D ⥤ C} {H : D ⥤ E} {I : E ⥤ D} (adj₁ : F ⊣ G) (adj₂ : H ⊣ I) set_option backward.isDefEq.respectTransparency false in /-- Composition of adjunctions. -/ -@[simps! -isSimp unit counit, stacks 0DV0] +@[to_dual self (reorder := C E, 2 6, F I, G H, adj₁ adj₂), simps! -isSimp unit counit, stacks 0DV0] def comp : F ⋙ H ⊣ I ⋙ G := mk' { homEquiv := fun _ _ ↦ Equiv.trans (adj₂.homEquiv _ _) (adj₁.homEquiv _ _) @@ -589,13 +589,12 @@ def comp : F ⋙ H ⊣ I ⋙ G := counit := (associator _ _ _).inv ≫ whiskerRight ((associator _ _ _).hom ≫ whiskerLeft _ adj₁.counit ≫ I.rightUnitor.hom) _ ≫ adj₂.counit } -@[simp, reassoc] -lemma comp_unit_app (X : C) : +lemma comp_unit_app (X : C) : dsimp% (adj₁.comp adj₂).unit.app X = adj₁.unit.app X ≫ G.map (adj₂.unit.app (F.obj X)) := by simp [Adjunction.comp] -@[simp, reassoc] -lemma comp_counit_app (X : E) : +@[to_dual existing (attr := simp, reassoc) comp_unit_app] +lemma comp_counit_app (X : E) : dsimp% (adj₁.comp adj₂).counit.app X = H.map (adj₁.counit.app (I.obj X)) ≫ adj₂.counit.app X := by simp [Adjunction.comp] @@ -799,6 +798,42 @@ instance (priority := 10) isRightAdjoint_of_isEquivalence {F : C ⥤ D} [F.IsEqu IsRightAdjoint F := F.asEquivalence.isRightAdjoint_functor +lemma isLeftAdjoint_comp_iff_right {E : Type u₃} [Category.{v₃} E] (F : C ⥤ D) (G : D ⥤ E) + [F.IsEquivalence] : + (F ⋙ G).IsLeftAdjoint ↔ G.IsLeftAdjoint := by + refine ⟨fun h ↦ ?_, fun h ↦ inferInstance⟩ + let iso : G ≅ F.asEquivalence.inverse ⋙ F ⋙ G := + (Functor.leftUnitor _).symm ≪≫ Functor.isoWhiskerRight (F.asEquivalence.counitIso).symm _ ≪≫ + Functor.associator _ _ _ + exact isLeftAdjoint_of_iso iso.symm + +lemma isRightAdjoint_comp_iff_right {E : Type u₃} [Category.{v₃} E] (F : C ⥤ D) (G : D ⥤ E) + [F.IsEquivalence] : + (F ⋙ G).IsRightAdjoint ↔ G.IsRightAdjoint := by + refine ⟨fun h ↦ ?_, fun h ↦ inferInstance⟩ + let iso : G ≅ F.asEquivalence.inverse ⋙ F ⋙ G := + (Functor.leftUnitor _).symm ≪≫ Functor.isoWhiskerRight (F.asEquivalence.counitIso).symm _ ≪≫ + Functor.associator _ _ _ + exact isRightAdjoint_of_iso iso.symm + +lemma isLeftAdjoint_comp_iff_left {E : Type u₃} [Category.{v₃} E] (F : C ⥤ D) (G : D ⥤ E) + [G.IsEquivalence] : + (F ⋙ G).IsLeftAdjoint ↔ F.IsLeftAdjoint := by + refine ⟨fun h ↦ ?_, fun h ↦ inferInstance⟩ + let iso : F ≅ (F ⋙ G) ⋙ G.asEquivalence.inverse := + (Functor.rightUnitor _).symm ≪≫ Functor.isoWhiskerLeft _ G.asEquivalence.unitIso ≪≫ + (Functor.associator _ _ _).symm + exact isLeftAdjoint_of_iso iso.symm + +lemma isRightAdjoint_comp_iff_left {E : Type u₃} [Category.{v₃} E] (F : C ⥤ D) (G : D ⥤ E) + [G.IsEquivalence] : + (F ⋙ G).IsRightAdjoint ↔ F.IsRightAdjoint := by + refine ⟨fun h ↦ ?_, fun h ↦ inferInstance⟩ + let iso : F ≅ (F ⋙ G) ⋙ G.asEquivalence.inverse := + (Functor.rightUnitor _).symm ≪≫ Functor.isoWhiskerLeft _ G.asEquivalence.unitIso ≪≫ + (Functor.associator _ _ _).symm + exact isRightAdjoint_of_iso iso.symm + end Functor end CategoryTheory diff --git a/Mathlib/CategoryTheory/Adjunction/Mates.lean b/Mathlib/CategoryTheory/Adjunction/Mates.lean index c458821adaf0d6..4e9e887d029919 100644 --- a/Mathlib/CategoryTheory/Adjunction/Mates.lean +++ b/Mathlib/CategoryTheory/Adjunction/Mates.lean @@ -523,7 +523,7 @@ lemma conjugateEquiv_associator_hom conjugateEquiv (adj₀₁.comp (adj₁₂.comp adj₂₃)) ((adj₀₁.comp adj₁₂).comp adj₂₃) (associator _ _ _).hom = (associator _ _ _).hom := by ext X - simp only [comp_obj, conjugateEquiv_apply_app, Adjunction.comp_unit_app, id_obj, + simp only [comp_obj, conjugateEquiv_apply_app, Adjunction.comp_unit_app, Functor.comp_map, Category.assoc, ← map_comp, associator_hom_app, map_id, Adjunction.comp_counit_app, Category.id_comp] simp @@ -557,7 +557,7 @@ lemma conjugateEquiv_whiskerRight conjugateEquiv (adj₁.comp adj) (adj₂.comp adj) (whiskerRight τ L) = whiskerLeft R (conjugateEquiv adj₁ adj₂ τ) := by ext X - simp only [comp_obj, conjugateEquiv_apply_app, comp_unit_app, id_obj, Functor.whiskerRight_app, + simp only [comp_obj, conjugateEquiv_apply_app, comp_unit_app, Functor.whiskerRight_app, Functor.comp_map, comp_counit_app, ← map_comp, assoc, Functor.whiskerLeft_app] simp diff --git a/Mathlib/CategoryTheory/Balanced.lean b/Mathlib/CategoryTheory/Balanced.lean index b6499a5f170c0f..d9f2b95a9f04f7 100644 --- a/Mathlib/CategoryTheory/Balanced.lean +++ b/Mathlib/CategoryTheory/Balanced.lean @@ -27,19 +27,19 @@ namespace CategoryTheory variable {C : Type u} [Category.{v} C] -section - -variable (C) - +variable (C) in /-- A category is called balanced if any morphism that is both monic and epic is an isomorphism. -/ class Balanced : Prop where isIso_of_mono_of_epi : ∀ {X Y : C} (f : X ⟶ Y) [Mono f] [Epi f], IsIso f -end +attribute [to_dual self (reorder := X Y, 7 8)] Balanced.isIso_of_mono_of_epi +attribute [to_dual self (reorder := isIso_of_mono_of_epi (X Y, 4 5))] Balanced.mk +@[to_dual self (reorder := X Y, 7 8)] theorem isIso_of_mono_of_epi [Balanced C] {X Y : C} (f : X ⟶ Y) [Mono f] [Epi f] : IsIso f := Balanced.isIso_of_mono_of_epi _ +@[to_dual isIso_iff_epi_and_mono] theorem isIso_iff_mono_and_epi [Balanced C] {X Y : C} (f : X ⟶ Y) : IsIso f ↔ Mono f ∧ Epi f := ⟨fun _ => ⟨inferInstance, inferInstance⟩, fun ⟨_, _⟩ => isIso_of_mono_of_epi _⟩ diff --git a/Mathlib/CategoryTheory/Bicategory/Adjunction/Adj.lean b/Mathlib/CategoryTheory/Bicategory/Adjunction/Adj.lean index c8a557f1e6ca79..52352ccc4076a4 100644 --- a/Mathlib/CategoryTheory/Bicategory/Adjunction/Adj.lean +++ b/Mathlib/CategoryTheory/Bicategory/Adjunction/Adj.lean @@ -131,23 +131,27 @@ def iso₂Mk {α β : a ⟶ b} (el : α.l ≅ β.l) (er : β.r ≅ α.r) namespace Bicategory +set_option linter.dupNamespace false in /-- The associator in the bicategory `Adj B`. -/ @[simps!] def associator (α : a ⟶ b) (β : b ⟶ c) (γ : c ⟶ d) : (α ≫ β) ≫ γ ≅ α ≫ β ≫ γ := iso₂Mk (α_ _ _ _) (α_ _ _ _) (conjugateEquiv_associator_hom _ _ _) +set_option linter.dupNamespace false in /-- The left unitor in the bicategory `Adj B`. -/ @[simps!] def leftUnitor (α : a ⟶ b) : 𝟙 a ≫ α ≅ α := iso₂Mk (λ_ _) (ρ_ _).symm (by simpa using conjugateEquiv_id_comp_right_apply α.adj α.adj (𝟙 _)) +set_option linter.dupNamespace false in /-- The right unitor in the bicategory `Adj B`. -/ @[simps!] def rightUnitor (α : a ⟶ b) : α ≫ 𝟙 b ≅ α := iso₂Mk (ρ_ _) (λ_ _).symm (by simpa using conjugateEquiv_comp_id_right_apply α.adj α.adj (𝟙 _)) +set_option linter.dupNamespace false in /-- The left whiskering in the bicategory `Adj B`. -/ @[simps] def whiskerLeft (α : a ⟶ b) {β β' : b ⟶ c} (y : β ⟶ β') : α ≫ β ⟶ α ≫ β' where @@ -156,6 +160,7 @@ def whiskerLeft (α : a ⟶ b) {β β' : b ⟶ c} (y : β ⟶ β') : α ≫ β conjugateEquiv_τl := by simp [conjugateEquiv_whiskerLeft, Hom₂.conjugateEquiv_τl] +set_option linter.dupNamespace false in /-- The right whiskering in the bicategory `Adj B`. -/ @[simps] def whiskerRight {α α' : a ⟶ b} (x : α ⟶ α') (β : b ⟶ c) : α ≫ β ⟶ α' ≫ β where diff --git a/Mathlib/CategoryTheory/Bicategory/Basic.lean b/Mathlib/CategoryTheory/Bicategory/Basic.lean index 130bb40e313fbe..99c3d8903325ad 100644 --- a/Mathlib/CategoryTheory/Bicategory/Basic.lean +++ b/Mathlib/CategoryTheory/Bicategory/Basic.lean @@ -48,6 +48,7 @@ universe w v u open Category Iso -- intended to be used with explicit universe parameters +set_option linter.checkUnivs false in /-- In a bicategory, we can compose the 1-morphisms `f : a ⟶ b` and `g : b ⟶ c` to obtain a 1-morphism `f ≫ g : a ⟶ c`. This composition does not need to be strictly associative, but there is a specified associator, `α_ f g h : (f ≫ g) ≫ h ≅ f ≫ (g ≫ h)`. @@ -57,7 +58,6 @@ These associators and unitors satisfy the pentagon and triangle equations. See https://ncatlab.org/nlab/show/bicategory. -/ -@[nolint checkUnivs] class Bicategory (B : Type u) extends CategoryStruct.{v} B where /-- The category structure on the collection of 1-morphisms -/ homCategory : ∀ a b : B, Category.{w} (a ⟶ b) := by infer_instance diff --git a/Mathlib/CategoryTheory/Bicategory/Functor/StrictlyUnitary.lean b/Mathlib/CategoryTheory/Bicategory/Functor/StrictlyUnitary.lean index 073d6dd9810ae8..9d80185dfd06a4 100644 --- a/Mathlib/CategoryTheory/Bicategory/Functor/StrictlyUnitary.lean +++ b/Mathlib/CategoryTheory/Bicategory/Functor/StrictlyUnitary.lean @@ -142,7 +142,7 @@ instance mapId_isIso (F : StrictlyUnitaryLaxFunctor B C) (x : B) : to an isomorphism when `F` is strictly unitary. -/ @[simps] def mapIdIso (F : StrictlyUnitaryLaxFunctor B C) (x : B) : - 𝟙 (F.obj x) ≅ F.map (𝟙 x) where + 𝟙 (F.obj x) ≅ F.map (𝟙 x) where hom := F.mapId x inv := eqToHom (F.map_id x) hom_inv_id := by simp [F.mapId_eq_eqToHom] diff --git a/Mathlib/CategoryTheory/Category/Cat.lean b/Mathlib/CategoryTheory/Category/Cat.lean index f1335563886635..d2b24eb2228105 100644 --- a/Mathlib/CategoryTheory/Category/Cat.lean +++ b/Mathlib/CategoryTheory/Category/Cat.lean @@ -31,8 +31,8 @@ namespace CategoryTheory open Bicategory Functor -- intended to be used with explicit universe parameters +set_option linter.checkUnivs false in /-- Category of categories. -/ -@[nolint checkUnivs] def Cat := Bundled Category.{v, u} diff --git a/Mathlib/CategoryTheory/Category/Preorder.lean b/Mathlib/CategoryTheory/Category/Preorder.lean index 6a0802ed2d49da..4f84c09560328b 100644 --- a/Mathlib/CategoryTheory/Category/Preorder.lean +++ b/Mathlib/CategoryTheory/Category/Preorder.lean @@ -80,7 +80,8 @@ theorem homOfLE_comp {x y z : X} (h : x ≤ y) (k : y ≤ z) : theorem leOfHom {x y : X} (h : x ⟶ y) : x ≤ y := h.down.down -@[nolint defLemma, inherit_doc leOfHom] +set_option linter.defProp false in +@[inherit_doc leOfHom] abbrev _root_.Quiver.Hom.le := @leOfHom @[simp] diff --git a/Mathlib/CategoryTheory/Category/Quiv.lean b/Mathlib/CategoryTheory/Category/Quiv.lean index 473229f413a7c4..0cdcf0680ed8c8 100644 --- a/Mathlib/CategoryTheory/Category/Quiv.lean +++ b/Mathlib/CategoryTheory/Category/Quiv.lean @@ -22,8 +22,8 @@ universe v u v₁ v₂ v₃ u₁ u₂ u₃ w namespace CategoryTheory -- intended to be used with explicit universe parameters +set_option linter.checkUnivs false in /-- Category of quivers. -/ -@[nolint checkUnivs] def Quiv := Bundled Quiver.{v, u} diff --git a/Mathlib/CategoryTheory/Category/ReflQuiv.lean b/Mathlib/CategoryTheory/Category/ReflQuiv.lean index 9dd8b7a23195f1..649de3f73a0344 100644 --- a/Mathlib/CategoryTheory/Category/ReflQuiv.lean +++ b/Mathlib/CategoryTheory/Category/ReflQuiv.lean @@ -21,8 +21,8 @@ The category `ReflQuiv` of (bundled) reflexive quivers, and the free/forgetful a namespace CategoryTheory universe v u v₁ v₂ u₁ u₂ +set_option linter.checkUnivs false in /-- Category of refl quivers. -/ -@[nolint checkUnivs] def ReflQuiv := Bundled ReflQuiver.{v, u} diff --git a/Mathlib/CategoryTheory/Comma/Arrow.lean b/Mathlib/CategoryTheory/Comma/Arrow.lean index 767eae2fdee3b1..5ea218a8f31358 100644 --- a/Mathlib/CategoryTheory/Comma/Arrow.lean +++ b/Mathlib/CategoryTheory/Comma/Arrow.lean @@ -33,7 +33,10 @@ variable (T) in squares in `T`. -/ def Arrow := Comma (𝟭 T) (𝟭 T) +to_dual_name_hint Left Right + /-- The type of morphisms in the category `Arrow T`. -/ +@[to_dual self (reorder := f g)] protected def Arrow.Hom (f g : Arrow T) := CommaMorphism f g instance : Quiver (Arrow T) where @@ -45,85 +48,79 @@ instance : Category (Arrow T) := namespace Arrow /-- The left object of an arrow. -/ +@[to_dual /-- The right object of an arrow. -/] abbrev left (X : Arrow T) : T := Comma.left X -/-- The right object of an arrow. -/ -abbrev right (X : Arrow T) : T := Comma.right X - /-- Given `X : Arrow T`, this is the morphism `X.left ⟶ X.right`. -/ abbrev hom (X : Arrow T) : X.left ⟶ X.right := Comma.hom X /-- The left part of a morphism in the category of arrows. -/ +@[to_dual /-- The right part of a morphism in the category of arrows. -/] abbrev Hom.left {X Y : Arrow T} (f : X ⟶ Y) : X.left ⟶ Y.left := CommaMorphism.left f -/-- The right part of a morphism in the category of arrows. -/ -abbrev Hom.right {X Y : Arrow T} (f : X ⟶ Y) : X.right ⟶ Y.right := CommaMorphism.right f - -@[ext] +@[ext, to_dual self (reorder := X Y, h₁ h₂)] lemma hom_ext {X Y : Arrow T} (f g : X ⟶ Y) (h₁ : f.left = g.left) (h₂ : f.right = g.right) : f = g := CommaMorphism.ext h₁ h₂ -@[simp] +@[to_dual (attr := simp)] theorem id_left (f : Arrow T) : Arrow.Hom.left (𝟙 f) = 𝟙 f.left := rfl -@[simp] -theorem id_right (f : Arrow T) : Arrow.Hom.right (𝟙 f) = 𝟙 f.right := - rfl - -@[simp, reassoc] +@[to_dual (reorder := f g) (attr := simp, reassoc)] theorem comp_left {X Y Z : Arrow T} (f : X ⟶ Y) (g : Y ⟶ Z) : (f ≫ g).left = f.left ≫ g.left := rfl -@[simp, reassoc] -theorem comp_right {X Y Z : Arrow T} (f : X ⟶ Y) (g : Y ⟶ Z) : - (f ≫ g).right = f.right ≫ g.right := rfl - /-- An object in the arrow category is simply a morphism in `T`. -/ -@[simps] +@[simps, to_dual self] def mk {X Y : T} (f : X ⟶ Y) : Arrow T where left := X right := Y hom := f +attribute [to_dual existing] mk_left +attribute [to_dual self] mk_hom + @[simp] theorem mk_eq (f : Arrow T) : Arrow.mk f.hom = f := by cases f rfl +@[to_dual none] lemma mk_surjective (f : Arrow T) : ∃ (X Y : T) (g : X ⟶ Y), f = Arrow.mk g := ⟨_, _, f.hom, rfl⟩ +@[to_dual self] theorem mk_injective (A B : T) : Function.Injective (Arrow.mk : (A ⟶ B) → Arrow T) := fun f g h => by cases h rfl +@[to_dual self] theorem mk_inj (A B : T) {f g : A ⟶ B} : Arrow.mk f = Arrow.mk g ↔ f = g := (mk_injective A B).eq_iff +@[to_dual self] instance {X Y : T} : CoeOut (X ⟶ Y) (Arrow T) where coe := mk -@[reassoc (attr := simp high)] +@[to_dual none, reassoc (attr := simp high)] theorem w {f g : Arrow T} (sq : f ⟶ g) : sq.left ≫ g.hom = f.hom ≫ sq.right := CommaMorphism.w sq -@[reassoc] -lemma Hom.w {f g : Arrow T} (sq : f ⟶ g) : sq.left ≫ g.hom = f.hom ≫ sq.right := by - simp +@[to_dual none, reassoc] +alias Hom.w := w +@[to_dual] theorem hom.congr_left {f g : Arrow T} {φ₁ φ₂ : f ⟶ g} (h : φ₁ = φ₂) : φ₁.left = φ₂.left := by rw [h] -theorem hom.congr_right {f g : Arrow T} {φ₁ φ₂ : f ⟶ g} (h : φ₁ = φ₂) : φ₁.right = φ₂.right := by - simp [h] - +@[to_dual none] theorem iso_w {f g : Arrow T} (e : f ≅ g) : g.hom = e.inv.left ≫ f.hom ≫ e.hom.right := by simp [← Arrow.comp_right] +@[to_dual none] theorem iso_w' {W X Y Z : T} {f : W ⟶ X} {g : Y ⟶ Z} (e : Arrow.mk f ≅ Arrow.mk g) : g = e.inv.left ≫ f ≫ e.hom.right := iso_w e @@ -171,6 +168,14 @@ def homMk {f g : Arrow T} (u : f.left ⟶ g.left) (v : f.right ⟶ g.right) right := v w := w +/-- `homMk''` is the dual of `homMk`, which we need for `to_dual`. +Please avoid using this directly. -/ +@[to_dual existing homMk] +abbrev homMk'' {f g : Arrow T} (u : g.right ⟶ f.right) (v : g.left ⟶ f.left) + (w : g.hom ≫ u = v ≫ f.hom := by cat_disch) : g ⟶ f := + homMk v u +attribute [to_dual none] homMk_left homMk_right + /-- We can also build a morphism in the arrow category out of any commutative square in `T`. -/ @[simps] def homMk' {X Y : T} {f : X ⟶ Y} {P Q : T} {g : P ⟶ Q} (u : X ⟶ P) (v : Y ⟶ Q) @@ -180,24 +185,33 @@ def homMk' {X Y : T} {f : X ⟶ Y} {P Q : T} {g : P ⟶ Q} (u : X ⟶ P) (v : Y right := v w := w +/-- `homMk'''` is the dual of `homMk'`, which we need for `to_dual`. +Please avoid using this directly. -/ +@[to_dual existing homMk'] +abbrev homMk''' {X Y : T} {f : Y ⟶ X} {P Q : T} {g : Q ⟶ P} (u : P ⟶ X) (v : Q ⟶ Y) + (w : g ≫ u = v ≫ f := by cat_disch) : mk g ⟶ mk f := + homMk' v u +attribute [to_dual none] homMk'_left + set_option backward.defeqAttrib.useBackward true in -@[reassoc] +@[to_dual none, reassoc] theorem w_mk_left {X Y : T} {f : X ⟶ Y} {g : Arrow T} (sq : mk f ⟶ g) : dsimp% sq.left ≫ g.hom = f ≫ sq.right := sq.w set_option backward.defeqAttrib.useBackward true in -@[reassoc (attr := simp)] +@[to_dual none, reassoc (attr := simp)] theorem w_mk_right {f : Arrow T} {X Y : T} {g : X ⟶ Y} (sq : f ⟶ mk g) : dsimp% sq.left ≫ g = f.hom ≫ sq.right := sq.w set_option backward.defeqAttrib.useBackward true in -@[reassoc] +@[to_dual none, reassoc] theorem w_mk {X Y X' Y' : T} {f : X ⟶ Y} {g : X' ⟶ Y'} (sq : mk f ⟶ mk g) : dsimp% sq.left ≫ g = f ≫ sq.right := sq.w +@[to_dual self (reorder := f g, 6 7)] theorem isIso_of_isIso_left_of_isIso_right {f g : Arrow T} (ff : f ⟶ g) [IsIso ff.left] [IsIso ff.right] : IsIso ff where out := ⟨homMk (inv ff.left) (inv ff.right), by cat_disch⟩ @@ -210,59 +224,70 @@ def isoMk {f g : Arrow T} (l : f.left ≅ g.left) (r : f.right ≅ g.right) (h : l.hom ≫ g.hom = f.hom ≫ r.hom := by cat_disch) : f ≅ g := Comma.isoMk l r h +/-- `isoMk''` is the dual of `isoMk`, which we need for `to_dual`. +Please avoid using this directly. -/ +@[to_dual existing isoMk] +abbrev isoMk'' {f g : Arrow T} (l : f.right ≅ g.right) (r : f.left ≅ g.left) + (h : g.hom ≫ l.inv = r.inv ≫ f.hom := by cat_disch) : f ≅ g := + isoMk r l (by rwa [Iso.comp_inv_eq, Category.assoc, Iso.eq_inv_comp] at h) +attribute [to_dual none] isoMk_hom_left isoMk_hom_right isoMk_inv_left isoMk_inv_right + /-- A variant of `Arrow.isoMk` that creates an iso between two `Arrow.mk`s with a better type signature. -/ abbrev isoMk' {W X Y Z : T} (f : W ⟶ X) (g : Y ⟶ Z) (e₁ : W ≅ Y) (e₂ : X ≅ Z) (h : e₁.hom ≫ g = f ≫ e₂.hom := by cat_disch) : Arrow.mk f ≅ Arrow.mk g := Arrow.isoMk e₁ e₂ h +/-- `isoMk'''` is the dual of `isoMk'`, which we need for `to_dual`. +Please avoid using this directly. -/ +@[to_dual existing isoMk'] +abbrev isoMk''' {W X Y Z : T} (f : X ⟶ W) (g : Z ⟶ Y) (e₁ : W ≅ Y) + (e₂ : X ≅ Z) (h : g ≫ e₁.inv = e₂.inv ≫ f := by cat_disch) : mk f ≅ mk g := + isoMk' f g e₂ e₁ (by rwa [Iso.comp_inv_eq, Category.assoc, Iso.eq_inv_comp] at h) + section variable {f g : Arrow T} (sq : f ⟶ g) +@[to_dual] instance isIso_left [IsIso sq] : IsIso sq.left := ⟨(inv sq).left, by simp [← comp_left]⟩ -instance isIso_right [IsIso sq] : IsIso sq.right := - ⟨(inv sq).right, by simp [← comp_right]⟩ - -lemma isIso_of_isIso' {f g : Arrow T} (sq : f ⟶ g) [IsIso sq] [IsIso f.hom] : +@[to_dual none] +private lemma isIso_of_isIso' {f g : Arrow T} (sq : f ⟶ g) [IsIso sq] [IsIso f.hom] : IsIso g.hom := by rw [iso_w (asIso sq)] infer_instance -lemma isIso_of_isIso {X Y : T} {f : X ⟶ Y} {g : Arrow T} (sq : mk f ⟶ g) [IsIso sq] [IsIso f] : - IsIso g.hom := by - have : IsIso (mk f).hom := by assumption - apply isIso_of_isIso' sq - +@[to_dual none] lemma isIso_hom_iff_isIso_hom_of_isIso {f g : Arrow T} (sq : f ⟶ g) [IsIso sq] : IsIso f.hom ↔ IsIso g.hom := ⟨fun _ => isIso_of_isIso' sq, fun _ => isIso_of_isIso' (inv sq)⟩ +@[to_dual none] lemma isIso_iff_isIso_of_isIso {W X Y Z : T} {f : W ⟶ X} {g : Y ⟶ Z} (sq : mk f ⟶ mk g) [IsIso sq] : IsIso f ↔ IsIso g := isIso_hom_iff_isIso_hom_of_isIso sq +@[to_dual none] lemma isIso_hom_iff_isIso_of_isIso {Y Z : T} {f : Arrow T} {g : Y ⟶ Z} (sq : f ⟶ mk g) [IsIso sq] : IsIso f.hom ↔ IsIso g := isIso_hom_iff_isIso_hom_of_isIso sq -@[simp] +@[to_dual (attr := simp, push ←)] theorem inv_left [IsIso sq] : (inv sq).left = inv sq.left := IsIso.eq_inv_of_hom_inv_id (by simp [← comp_left]) -@[simp] -theorem inv_right [IsIso sq] : (inv sq).right = inv sq.right := - IsIso.eq_inv_of_hom_inv_id (by simp [← comp_right]) - +@[to_dual none] theorem left_hom_inv_right [IsIso sq] : sq.left ≫ g.hom ≫ inv sq.right = f.hom := by simp only [← Category.assoc, IsIso.comp_inv_eq, w] +@[to_dual none] theorem inv_left_hom_right [IsIso sq] : inv sq.left ≫ f.hom ≫ sq.right = g.hom := by simp only [w, IsIso.inv_comp_eq] set_option backward.defeqAttrib.useBackward true in +@[to_dual epi_right] instance mono_left [Mono sq] : Mono sq.left where right_cancellation {Z} φ ψ h := by let aux : (Z ⟶ f.left) → (Arrow.mk (𝟙 Z) ⟶ f) := fun φ => @@ -276,34 +301,14 @@ instance mono_left [Mono sq] : Mono sq.left where · exact h · simp [this, ← Arrow.w_mk_right, reassoc_of% h] -set_option backward.defeqAttrib.useBackward true in -instance epi_right [Epi sq] : Epi sq.right where - left_cancellation {Z} φ ψ h := by - let aux : (g.right ⟶ Z) → (g ⟶ Arrow.mk (𝟙 Z)) := fun φ => - Arrow.homMk (g.hom ≫ φ) φ - change (aux φ).right = (aux ψ).right - congr 1 - rw [← cancel_epi sq] - ext - · simp only [comp_left, comp_left, aux, mk_left, homMk_left, w_assoc, h] - · exact h - -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp))] lemma hom_inv_id_left (e : f ≅ g) : e.hom.left ≫ e.inv.left = 𝟙 _ := by rw [← comp_left, e.hom_inv_id, id_left] -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp))] lemma inv_hom_id_left (e : f ≅ g) : e.inv.left ≫ e.hom.left = 𝟙 _ := by rw [← comp_left, e.inv_hom_id, id_left] -@[reassoc (attr := simp)] -lemma hom_inv_id_right (e : f ≅ g) : e.hom.right ≫ e.inv.right = 𝟙 _ := by - rw [← comp_right, e.hom_inv_id, id_right] - -@[reassoc (attr := simp)] -lemma inv_hom_id_right (e : f ≅ g) : e.inv.right ≫ e.hom.right = 𝟙 _ := by - rw [← comp_right, e.inv_hom_id, id_right] - end /-- Given a square from an arrow `i` to an isomorphism `p`, express the source part of `sq` @@ -338,15 +343,10 @@ def squareToSnd {X Y Z : C} {i : Arrow C} {f : X ⟶ Y} {g : Y ⟶ Z} (sq : i Arrow.homMk (sq.left ≫ f) (sq.right) (by simp [w_mk sq]) /-- The functor sending an arrow to its source. -/ -@[simps!] +@[to_dual (attr := simps!) /-- The functor sending an arrow to its target. -/] def leftFunc : Arrow C ⥤ C := Comma.fst _ _ -/-- The functor sending an arrow to its target. -/ -@[simps!] -def rightFunc : Arrow C ⥤ C := - Comma.snd _ _ - set_option backward.defeqAttrib.useBackward true in /-- The natural transformation from `leftFunc` to `rightFunc`, given by the arrow itself. -/ @[simps] @@ -365,7 +365,9 @@ set_option backward.defeqAttrib.useBackward true in @[simps] def mapArrow (F : C ⥤ D) : Arrow C ⥤ Arrow D where obj a := Arrow.mk (F.map a.hom) - map f := Arrow.homMk (F.map f.left) (F.map f.right) (by simp [← Functor.map_comp]) + map {X Y} f := Arrow.homMk (F.map f.left) (F.map f.right) (by simp [← Functor.map_comp]) + +attribute [to_dual self (reorder := X Y)] mapArrow_map variable (C D) @@ -375,7 +377,9 @@ a functor `F : C ⥤ D` to `F.mapArrow`. -/ @[simps] def mapArrowFunctor : (C ⥤ D) ⥤ (Arrow C ⥤ Arrow D) where obj F := F.mapArrow - map τ := { app f := Arrow.homMk (τ.app _) (τ.app _) } + map {X Y} τ := { app f := Arrow.homMk (τ.app _) (τ.app _) } + +attribute [to_dual self (reorder := X Y)] mapArrowFunctor_map_app variable {C D} @@ -431,6 +435,7 @@ def Arrow.discreteEquiv (S : Type u) : Arrow (Discrete S) ≃ S where /-- Extensionality lemma for functors `C ⥤ D` which uses as an assumption that the induced maps `Arrow C → Arrow D` coincide. -/ +@[to_dual self] lemma Arrow.functor_ext {F G : C ⥤ D} (h : ∀ ⦃X Y : C⦄ (f : X ⟶ Y), F.mapArrow.obj (Arrow.mk f) = G.mapArrow.obj (Arrow.mk f)) : F = G := diff --git a/Mathlib/CategoryTheory/Comma/Final.lean b/Mathlib/CategoryTheory/Comma/Final.lean index 471c7c667af357..04608f5d124a3e 100644 --- a/Mathlib/CategoryTheory/Comma/Final.lean +++ b/Mathlib/CategoryTheory/Comma/Final.lean @@ -5,12 +5,8 @@ Authors: Jakob von Raumer -/ module -public import Mathlib.CategoryTheory.Functor.KanExtension.Adjunction public import Mathlib.CategoryTheory.Limits.IsConnected -public import Mathlib.CategoryTheory.Limits.Sifted public import Mathlib.CategoryTheory.Filtered.Final -public import Mathlib.CategoryTheory.Filtered.Flat -public import Mathlib.CategoryTheory.Grothendieck public import Mathlib.CategoryTheory.Comma.StructuredArrow.CommaMap /-! @@ -41,60 +37,95 @@ namespace Comma open Limits Functor CostructuredArrow -section Small - -variable {A : Type v₁} [Category.{v₁} A] -variable {B : Type v₁} [Category.{v₁} B] -variable {T : Type v₁} [Category.{v₁} T] -variable (L : A ⥤ T) (R : B ⥤ T) - -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in -private lemma final_fst_small [R.Final] : (fst L R).Final := by - rw [Functor.final_iff_isIso_colimit_pre] - intro G - let i : colimit G ≅ colimit (fst L R ⋙ G) := - colimitIsoColimitGrothendieck L G ≪≫ - (Final.colimitIso (Grothendieck.pre (functor L) R) (grothendieckProj L ⋙ G)).symm ≪≫ - HasColimit.isoOfNatIso (Iso.refl _) ≪≫ - Final.colimitIso (grothendieckPrecompFunctorEquivalence L R).functor (fst L R ⋙ G) - convert! i.isIso_inv - apply colimit.hom_ext - intro ⟨a, b, f⟩ - simp only [colimit.ι_pre, comp_obj, fst_obj, grothendieckPrecompFunctorEquivalence_functor, - Iso.trans_inv, Iso.symm_inv, Category.assoc, i] - change _ = colimit.ι (fst L R ⋙ G) - ((grothendieckPrecompFunctorToComma L R).obj ⟨b, CostructuredArrow.mk f⟩) ≫ _ - simp - -end Small - -section NonSmall - variable {A : Type u₁} [Category.{v₁} A] variable {B : Type u₂} [Category.{v₂} B] variable {T : Type u₃} [Category.{v₃} T] variable (L : A ⥤ T) (R : B ⥤ T) -instance final_fst [R.Final] : (fst L R).Final := by - let sA : A ≌ AsSmall.{max u₁ u₂ u₃ v₁ v₂ v₃} A := AsSmall.equiv - let sB : B ≌ AsSmall.{max u₁ u₂ u₃ v₁ v₂ v₃} B := AsSmall.equiv - let sT : T ≌ AsSmall.{max u₁ u₂ u₃ v₁ v₂ v₃} T := AsSmall.equiv - let L' := sA.inverse ⋙ L ⋙ sT.functor - let R' := sB.inverse ⋙ R ⋙ sT.functor - let fC : Comma L R ⥤ Comma L' R' := - map (F₁ := sA.functor) (F := sT.functor) (F₂ := sB.functor) - (isoWhiskerRight sA.unitIso (L ⋙ sT.functor)).hom - (isoWhiskerRight sB.unitIso (R ⋙ sT.functor)).hom - have : Final (fst L' R') := final_fst_small _ _ - apply final_of_natIso (F := (fC ⋙ fst L' R' ⋙ sA.inverse)) - exact (Functor.associator _ _ _).symm.trans (Iso.compInverseIso (mapFst _ _)) - -instance initial_snd [L.Initial] : (snd L R).Initial := by - have : ((opFunctor L R).leftOp ⋙ fst R.op L.op).Final := - final_equivalence_comp (opEquiv L R).functor.leftOp (fst R.op L.op) - have : (snd L R).op.Final := final_of_natIso (opFunctorCompFst _ _) - apply initial_of_final_op +section Relative + +lemma isCofiltered_of_isCofiltered_costructuredArrow [IsCofiltered A] [IsCofiltered B] + [∀ b, IsCofiltered (CostructuredArrow L (R.obj b))] : IsCofiltered (Comma L R) where + nonempty := by + obtain ⟨b⟩ := IsCofiltered.nonempty (C := B) + obtain ⟨X⟩ : Nonempty (CostructuredArrow L (R.obj b)) := IsCofiltered.nonempty + exact ⟨⟨X.left, b, X.hom⟩⟩ + toIsCofilteredOrEmpty := by + refine ⟨fun j₁ j₂ ↦ ?_, fun j₁ j₂ u v ↦ ?_⟩ + · obtain ⟨Q⟩ : Nonempty (CostructuredArrow L (R.obj (IsCofiltered.min j₁.right j₂.right))) := + IsCofiltered.nonempty + obtain ⟨ia, va₁, va₂, heqa⟩ := exists_eq_of_isCofiltered_costructuredArrow L + (Q.hom ≫ R.map (IsCofiltered.minToLeft j₁.right j₂.right)) j₁.hom + obtain ⟨ib, vb₁, vb₂, heqb⟩ := exists_eq_of_isCofiltered_costructuredArrow L + (Q.hom ≫ R.map (IsCofiltered.minToRight j₁.right j₂.right)) j₂.hom + obtain ⟨i₀, il₀, ir₀, heq⟩ := IsCofiltered.cospan va₁ vb₁ + exact ⟨⟨i₀, IsCofiltered.min j₁.right j₂.right, L.map (il₀ ≫ va₁) ≫ Q.hom⟩, + ⟨il₀ ≫ va₂, IsCofiltered.minToLeft _ _, by simp [← heqa]⟩, + ⟨ir₀ ≫ vb₂, IsCofiltered.minToRight _ _, by cat_disch⟩, trivial⟩ + · obtain ⟨Q⟩ : Nonempty (CostructuredArrow L (R.obj (IsCofiltered.eq u.right v.right))) := + IsCofiltered.nonempty + obtain ⟨ia, va₁, va₂, heqa⟩ := exists_eq_of_isCofiltered_costructuredArrow L + (Q.hom ≫ R.map (IsCofiltered.eqHom u.right v.right)) j₁.hom + obtain ⟨i₀, α, β, hα, hβ⟩ := IsCofiltered.bowtie u.left (va₂ ≫ v.left) (𝟙 _) va₂ + have := IsCofiltered.eq_condition u.right v.right + exact ⟨⟨i₀, IsCofiltered.eq u.right v.right, L.map (β ≫ va₁) ≫ Q.hom⟩, + ⟨β ≫ va₂, IsCofiltered.eqHom u.right v.right, by cat_disch⟩, by cat_disch⟩ + +set_option backward.isDefEq.respectTransparency false in +lemma initial_fst_of_isCofiltered_costructuredArrow [IsCofiltered A] [IsCofiltered B] + [∀ b, IsCofiltered (CostructuredArrow L (R.obj b))] : (fst L R).Initial := by + have := isCofiltered_of_isCofiltered_costructuredArrow L R + rw [Functor.initial_iff_of_isCofiltered] + refine ⟨fun a ↦ ?_, fun {a} A' s s' ↦ ?_⟩ + · obtain ⟨b⟩ := IsCofiltered.nonempty (C := B) + obtain ⟨X⟩ : Nonempty (CostructuredArrow L (R.obj b)) := IsCofiltered.nonempty + exact ⟨⟨IsCofiltered.min a X.left, b, L.map (IsCofiltered.minToRight a X.left) ≫ X.hom⟩, + ⟨IsCofiltered.minToLeft a X.left⟩⟩ + · exact ⟨⟨_, A'.right, L.map (IsCofiltered.eqHom s s') ≫ A'.hom⟩, + ⟨IsCofiltered.eqHom s s', 𝟙 A'.right, by simp⟩, IsCofiltered.eq_condition s s'⟩ + +lemma initial_snd_of_isConnected_costructuredArrow + [∀ b, IsConnected (CostructuredArrow L (R.obj b))] : (snd L R).Initial where + out b := by + have := final_of_adjunction (costructuredArrowSndAdjunction L R b) + rw [← isConnected_iff_of_final (costructuredArrowSndInclusion L R b)] + infer_instance + +lemma isFiltered_of_isFiltered_structuredArrow [IsFiltered A] [IsFiltered B] + [∀ a, IsFiltered (StructuredArrow (L.obj a) R)] : IsFiltered (Comma L R) := by + have (a : Aᵒᵖ) : IsCofiltered (CostructuredArrow R.op (L.op.obj a)) := + IsCofiltered.of_equivalence (structuredArrowOpEquivalence R (L.obj a.unop)) + have : IsCofiltered (Comma R.op L.op) := isCofiltered_of_isCofiltered_costructuredArrow _ _ + exact IsFiltered.of_equivalence (opEquiv L R).symm + +lemma final_fst_of_isConnected_structuredArrow + [∀ a, IsConnected (StructuredArrow (L.obj a) R)] : (fst L R).Final := by + have (a : Aᵒᵖ) : IsConnected (CostructuredArrow R.op (L.op.obj a)) := + (isConnected_iff_of_equivalence (structuredArrowOpEquivalence R (L.obj a.unop))).mp + inferInstance + have : (snd R.op L.op).Initial := initial_snd_of_isConnected_costructuredArrow _ _ + have : ((opFunctor L R).leftOp ⋙ snd R.op L.op).Initial := + initial_equivalence_comp (opEquiv L R).functor.leftOp _ + have : (fst L R).op.Initial := initial_of_natIso <| opFunctorCompSnd _ _ + apply final_of_initial_op + +lemma final_snd_of_isFiltered_structuredArrow [IsFiltered A] [IsFiltered B] + [∀ a, IsFiltered (StructuredArrow (L.obj a) R)] : (snd L R).Final := by + have (a : Aᵒᵖ) : IsCofiltered (CostructuredArrow R.op (L.op.obj a)) := + IsCofiltered.of_equivalence (structuredArrowOpEquivalence R (L.obj a.unop)) + have : (fst R.op L.op).Initial := initial_fst_of_isCofiltered_costructuredArrow _ _ + have : ((opFunctor L R).leftOp ⋙ fst R.op L.op).Initial := + initial_equivalence_comp (opEquiv L R).functor.leftOp _ + have : (snd L R).op.Initial := initial_of_natIso <| opFunctorCompFst _ _ + apply final_of_initial_op + +end Relative + +instance initial_snd [L.Initial] : (snd L R).Initial := + initial_snd_of_isConnected_costructuredArrow L R + +instance final_fst [R.Final] : (fst L R).Final := + final_fst_of_isConnected_structuredArrow L R /-- `Comma L R` with `L : A ⥤ T` and `R : B ⥤ T` is connected if `R` is final and `A` is connected. -/ @@ -106,8 +137,6 @@ connected. -/ instance isConnected_comma_of_initial [IsConnected B] [L.Initial] : IsConnected (Comma L R) := by rwa [isConnected_iff_of_initial (snd L R)] -end NonSmall - set_option backward.defeqAttrib.useBackward true in /-- Let the following diagram commute up to isomorphism: @@ -148,54 +177,30 @@ lemma map_final {A : Type u₁} [Category.{v₁} A] {B : Type u₂} [Category.{v section Filtered -variable {A : Type u₁} [Category.{v₁} A] -variable {B : Type u₂} [Category.{v₂} B] -variable {T : Type u₃} [Category.{v₃} T] -variable (L : A ⥤ T) (R : B ⥤ T) - -set_option backward.defeqAttrib.useBackward true in -attribute [local instance] map_final in /-- Let `A` and `B` be filtered categories, `R : B ⥤ T` be final and `L : A ⥤ T`. Then, the comma category `Comma L R` is filtered. -/ instance isFiltered_of_final [IsFiltered A] [IsFiltered B] [R.Final] : IsFiltered (Comma L R) := by - haveI (a : A) : IsFiltered (Comma (fromPUnit (L.obj a)) R) := - R.final_iff_isFiltered_structuredArrow.mp inferInstance (L.obj a) - have (a : A) : (fromPUnit (Over.mk (𝟙 a))).Final := final_const_of_isTerminal Over.mkIdTerminal - let η (a : A) : fromPUnit (Over.mk (𝟙 a)) ⋙ Over.forget a ⋙ L ≅ fromPUnit (L.obj a) := - NatIso.ofComponents (fun _ => Iso.refl _) - have (a : A) := IsFiltered.of_final (map (L := fromPUnit (L.obj a)) (F := 𝟭 T) (η a).hom - ((Iso.refl (𝟭 B ⋙ R)).inv)) - have : RepresentablyCoflat (fst L R) := - ⟨fun a => IsFiltered.of_equivalence (CostructuredArrow.ofCommaFstEquivalence L R a).symm⟩ - apply isFiltered_of_representablyCoflat (fst L R) - -attribute [local instance] isFiltered_of_final in + have := R.final_iff_isFiltered_structuredArrow.mp inferInstance + exact isFiltered_of_isFiltered_structuredArrow L R + /-- Let `A` and `B` be cofiltered categories, `L : A ⥤ T` be initial and `R : B ⥤ T`. Then, the comma category `Comma L R` is cofiltered. -/ lemma isCofiltered_of_initial [IsCofiltered A] [IsCofiltered B] [L.Initial] : - IsCofiltered (Comma L R) := - IsCofiltered.of_equivalence (Comma.opEquiv _ _).symm + IsCofiltered (Comma L R) := by + have := L.initial_iff_isCofiltered_costructuredArrow.mp inferInstance + exact isCofiltered_of_isCofiltered_costructuredArrow L R -set_option backward.defeqAttrib.useBackward true in -attribute [local instance] final_of_isFiltered_of_pUnit in /-- Let `A` and `B` be filtered categories, `R : B ⥤ T` be final and `R : A ⥤ T`. Then, the projection `snd L R : Comma L R ⥤ B` is final. -/ instance final_snd [IsFiltered A] [IsFiltered B] [R.Final] : (snd L R).Final := by - let iL : star.{1} A ⋙ 𝟭 _ ≅ L ⋙ star _ := Iso.refl _ - let iR : 𝟭 B ⋙ star.{1} B ≅ R ⋙ star _ := Iso.refl _ - have := map_final iL iR - let s := (equivProd (𝟭 _) (star B)).trans <| prod.leftUnitorEquivalence B - let iS : map iL.hom iR.inv ⋙ s.functor ≅ snd L R := - NatIso.ofComponents (fun _ => Iso.refl _) (fun f => by simp [iL, iR, s]) - apply final_of_natIso iS + have := R.final_iff_isFiltered_structuredArrow.mp inferInstance + exact final_snd_of_isFiltered_structuredArrow L R /-- Let `A` and `B` be cofiltered categories, `L : A ⥤ T` be initial and `R : B ⥤ T`. Then, the projection `fst L R : Comma L R ⥤ A` is initial. -/ instance initial_fst [IsCofiltered A] [IsCofiltered B] [L.Initial] : (fst L R).Initial := by - have : ((opFunctor L R).leftOp ⋙ snd R.op L.op).Final := - final_equivalence_comp (opEquiv L R).functor.leftOp _ - have : (fst L R).op.Final := final_of_natIso <| opFunctorCompSnd _ _ - apply initial_of_final_op + have := L.initial_iff_isCofiltered_costructuredArrow.mp inferInstance + exact initial_fst_of_isCofiltered_costructuredArrow L R end Filtered diff --git a/Mathlib/CategoryTheory/Comma/Over/Pullback.lean b/Mathlib/CategoryTheory/Comma/Over/Pullback.lean index 4eb249b5a8a64d..45e64ac8d25c1c 100644 --- a/Mathlib/CategoryTheory/Comma/Over/Pullback.lean +++ b/Mathlib/CategoryTheory/Comma/Over/Pullback.lean @@ -87,6 +87,12 @@ def mapPullbackAdj {X Y : C} (f : X ⟶ Y) [HasPullbacksAlong f] : · simp · simpa using (Over.w v).symm } } +instance {X Y : C} (f : X ⟶ Y) [HasPullbacksAlong f] : (Over.map f).IsLeftAdjoint := + (Over.mapPullbackAdj f).isLeftAdjoint + +instance {X Y : C} (f : X ⟶ Y) [HasPullbacksAlong f] : (Over.pullback f).IsRightAdjoint := + (Over.mapPullbackAdj f).isRightAdjoint + set_option backward.isDefEq.respectTransparency false in /-- The pullback along an epi that's preserved under pullbacks is faithful. diff --git a/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean b/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean index 320b6d70cd3c9e..5e5e0000dd8dcf 100644 --- a/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean +++ b/Mathlib/CategoryTheory/Comma/StructuredArrow/Basic.lean @@ -5,6 +5,7 @@ Authors: Adam Topaz, Kim Morrison -/ module +public import Mathlib.CategoryTheory.Adjunction.Basic public import Mathlib.CategoryTheory.PUnit public import Mathlib.CategoryTheory.Limits.Shapes.IsTerminal public import Mathlib.CategoryTheory.Functor.EpiMono @@ -1271,4 +1272,43 @@ end end Prod +namespace Comma + +variable {A : Type u₁} [Category.{v₁} A] {B : Type u₂} [Category.{v₂} B] + {T : Type u₃} [Category.{v₃} T] (L : A ⥤ T) (R : B ⥤ T) + +set_option backward.defeqAttrib.useBackward true in +/-- The functor from the costructured arrow category on `snd L R` over `b : B` to the +costructured arrow category on `L` over `R.obj b`. It is left adjoint to +`costructuredArrowSndInclusion`, see `costructuredArrowSndAdjunction`. -/ +@[simps] +def costructuredArrowSndProj (b : B) : + CostructuredArrow (snd L R) b ⥤ CostructuredArrow L (R.obj b) where + obj X := CostructuredArrow.mk (X.left.hom ≫ R.map X.hom) + map f := CostructuredArrow.homMk f.left.left <| by + dsimp + rw [reassoc_of% f.left.w, ← R.map_comp, dsimp% CostructuredArrow.w f] + +set_option backward.defeqAttrib.useBackward true in +/-- The functor from the costructured arrow category on `L` over `R.obj b` to the costructured +arrow category on `snd L R` over `b : B`. -/ +@[simps] +def costructuredArrowSndInclusion (b : B) : + CostructuredArrow L (R.obj b) ⥤ CostructuredArrow (snd L R) b where + obj X := ⟨⟨X.left, b, X.hom⟩, ⟨⟨⟩⟩, 𝟙 b⟩ + map f := CostructuredArrow.homMk ⟨f.left, 𝟙 b, by simp⟩ (by simp) + +set_option backward.defeqAttrib.useBackward true in +/-- The functor `costructuredArrowSndProj` is left adjoint to `costructuredArrowSndInclusion`. -/ +@[simps] +def costructuredArrowSndAdjunction (b : B) : + costructuredArrowSndProj L R b ⊣ costructuredArrowSndInclusion L R b where + unit.app X := CostructuredArrow.homMk ⟨𝟙 X.left.left, X.hom, by simp⟩ (by simp) + unit.naturality _ _ f := by + have := CostructuredArrow.w f + cat_disch + counit.app X := CostructuredArrow.homMk (𝟙 X.left) (by simp) + +end Comma + end CategoryTheory diff --git a/Mathlib/CategoryTheory/DinatTrans.lean b/Mathlib/CategoryTheory/DinatTrans.lean index 1924ec1fe102c7..4a3d5405a4a801 100644 --- a/Mathlib/CategoryTheory/DinatTrans.lean +++ b/Mathlib/CategoryTheory/DinatTrans.lean @@ -9,6 +9,7 @@ public import Mathlib.CategoryTheory.Opposites /-! # Dinatural transformations + Dinatural transformations are special kinds of transformations between functors `F G : Cᵒᵖ ⥤ C ⥤ D` which depend both covariantly and contravariantly on the same category (also known as difunctors). diff --git a/Mathlib/CategoryTheory/Discrete/Basic.lean b/Mathlib/CategoryTheory/Discrete/Basic.lean index 7d03d8068c25b7..3f7a993158e56e 100644 --- a/Mathlib/CategoryTheory/Discrete/Basic.lean +++ b/Mathlib/CategoryTheory/Discrete/Basic.lean @@ -6,6 +6,7 @@ Authors: Stephen Morgan, Kim Morrison, Floris van Doorn module public import Mathlib.CategoryTheory.Pi.Basic +public import Mathlib.Data.Set.Image /-! # Discrete categories @@ -63,6 +64,9 @@ def discreteEquiv {α : Type u₁} : Discrete α ≃ α where left_inv := by cat_disch right_inv := by cat_disch +lemma Discrete.as_bijective {α : Type*} : (Discrete.as (α := α)).Bijective := + discreteEquiv.bijective + instance {α : Type u₁} [DecidableEq α] : DecidableEq (Discrete α) := discreteEquiv.decidableEq @@ -179,6 +183,10 @@ theorem functor_obj_eq_as {I : Type u₁} (F : I → C) (X : Discrete I) : (Discrete.functor F).obj X = F X.as := rfl +@[simp] +lemma range_functor {I : Type*} (X : I → C) : Set.range (Discrete.functor X).obj = Set.range X := by + simp [Discrete.functor, Set.range_comp, Discrete.as_bijective.surjective.range_eq] + @[ext] lemma functor_ext {I : Type u₁} {G F : Discrete I ⥤ C} (h : (i : I) → G.obj ⟨i⟩ = F.obj ⟨i⟩) : G = F := by diff --git a/Mathlib/CategoryTheory/EqToHom.lean b/Mathlib/CategoryTheory/EqToHom.lean index 803eb1b07b990e..84483ea2b59654 100644 --- a/Mathlib/CategoryTheory/EqToHom.lean +++ b/Mathlib/CategoryTheory/EqToHom.lean @@ -45,6 +45,12 @@ def eqToHom {C : Type u₁} [CategoryStruct.{v₁} C] {X Y : C} (p : X = Y) : rw [p] exact 𝟙 _ +/-- `eqToHom'` is the dual of `eqToHom`, which we need for `to_dual`. +Please avoid using this directly. -/ +@[to_dual existing eqToHom] +abbrev eqToHom' {C : Type u₁} [CategoryStruct.{v₁} C] {X Y : C} (p : X = Y) : Y ⟶ X := + eqToHom p.symm + @[simp] theorem eqToHom_refl {C : Type u₁} [CategoryStruct.{v₁} C] (X : C) (p : X = X) : eqToHom p = 𝟙 X := @@ -52,7 +58,7 @@ theorem eqToHom_refl {C : Type u₁} [CategoryStruct.{v₁} C] (X : C) (p : X = variable {C : Type u₁} [Category.{v₁} C] -@[reassoc (attr := simp)] +@[to_dual none, reassoc (attr := simp)] theorem eqToHom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) : eqToHom p ≫ eqToHom q = eqToHom (p.trans q) := by cases p @@ -60,95 +66,108 @@ theorem eqToHom_trans {X Y Z : C} (p : X = Y) (q : Y = Z) : simp /-- `eqToHom h` is heterogeneously equal to the identity of its domain. -/ +@[to_dual none] lemma eqToHom_heq_id_dom (X Y : C) (h : X = Y) : eqToHom h ≍ 𝟙 X := by subst h; rfl /-- `eqToHom h` is heterogeneously equal to the identity of its codomain. -/ +@[to_dual none] lemma eqToHom_heq_id_cod (X Y : C) (h : X = Y) : eqToHom h ≍ 𝟙 Y := by subst h; rfl /-- Two morphisms are conjugate via eqToHom if and only if they are heterogeneously equal. Note this used to be in the Functor namespace, where it doesn't belong. -/ +@[to_dual none] theorem conj_eqToHom_iff_heq {W X Y Z : C} (f : W ⟶ X) (g : Y ⟶ Z) (h : W = Y) (h' : X = Z) : f = eqToHom h ≫ g ≫ eqToHom h'.symm ↔ f ≍ g := by cases h cases h' simp +@[to_dual none] theorem conj_eqToHom_iff_heq' {C} [Category* C] {W X Y Z : C} (f : W ⟶ X) (g : Y ⟶ Z) (h : W = Y) (h' : Z = X) : f = eqToHom h ≫ g ≫ eqToHom h' ↔ f ≍ g := conj_eqToHom_iff_heq _ _ _ h'.symm +@[to_dual none] theorem comp_eqToHom_iff {X Y Y' : C} (p : Y = Y') (f : X ⟶ Y) (g : X ⟶ Y') : f ≫ eqToHom p = g ↔ f = g ≫ eqToHom p.symm := { mp h := by simp [← h] mpr h := by simp [eq_whisker h (eqToHom p)] } +@[to_dual none] theorem eqToHom_comp_iff {X X' Y : C} (p : X = X') (f : X ⟶ Y) (g : X' ⟶ Y) : eqToHom p ≫ g = f ↔ g = eqToHom p.symm ≫ f := { mp h := by simp [← h] mpr h := by simp [h] } +@[to_dual none] theorem eqToHom_comp_heq {C} [Category* C] {W X Y : C} (f : Y ⟶ X) (h : W = Y) : eqToHom h ≫ f ≍ f := by rw [← conj_eqToHom_iff_heq _ _ h rfl, eqToHom_refl, Category.comp_id] -@[simp] theorem eqToHom_comp_heq_iff {C} [Category* C] {W X Y Z Z' : C} +@[simp, to_dual none] +theorem eqToHom_comp_heq_iff {C} [Category* C] {W X Y Z Z' : C} (f : Y ⟶ X) (g : Z ⟶ Z') (h : W = Y) : eqToHom h ≫ f ≍ g ↔ f ≍ g := ⟨(eqToHom_comp_heq ..).symm.trans, (eqToHom_comp_heq ..).trans⟩ -@[simp] theorem heq_eqToHom_comp_iff {C} [Category* C] {W X Y Z Z' : C} +@[simp, to_dual none] +theorem heq_eqToHom_comp_iff {C} [Category* C] {W X Y Z Z' : C} (f : Y ⟶ X) (g : Z ⟶ Z') (h : W = Y) : g ≍ eqToHom h ≫ f ↔ g ≍ f := ⟨(·.trans (eqToHom_comp_heq ..)), (·.trans (eqToHom_comp_heq ..).symm)⟩ +@[to_dual none] theorem comp_eqToHom_heq {C} [Category* C] {X Y Z : C} (f : X ⟶ Y) (h : Y = Z) : f ≫ eqToHom h ≍ f := by rw [← conj_eqToHom_iff_heq' _ _ rfl h, eqToHom_refl, Category.id_comp] -@[simp] theorem comp_eqToHom_heq_iff {C} [Category* C] {W X Y Z Z' : C} +@[simp, to_dual none] +theorem comp_eqToHom_heq_iff {C} [Category* C] {W X Y Z Z' : C} (f : X ⟶ Y) (g : Z ⟶ Z') (h : Y = W) : f ≫ eqToHom h ≍ g ↔ f ≍ g := ⟨(comp_eqToHom_heq ..).symm.trans, (comp_eqToHom_heq ..).trans⟩ -@[simp] theorem heq_comp_eqToHom_iff {C} [Category* C] {W X Y Z Z' : C} +@[simp, to_dual none] +theorem heq_comp_eqToHom_iff {C} [Category* C] {W X Y Z Z' : C} (f : X ⟶ Y) (g : Z ⟶ Z') (h : Y = W) : g ≍ f ≫ eqToHom h ↔ g ≍ f := ⟨(·.trans (comp_eqToHom_heq ..)), (·.trans (comp_eqToHom_heq ..).symm)⟩ +@[to_dual self (reorder := X Z, X' Z', f g, f' g', eq1 eq3, H1 H2)] theorem heq_comp {C} [Category* C] {X Y Z X' Y' Z' : C} {f : X ⟶ Y} {g : Y ⟶ Z} {f' : X' ⟶ Y'} {g' : Y' ⟶ Z'} (eq1 : X = X') (eq2 : Y = Y') (eq3 : Z = Z') (H1 : f ≍ f') (H2 : g ≍ g') : f ≫ g ≍ f' ≫ g' := by - grind + congr! variable {β : Sort*} /-- We can push `eqToHom` to the left through families of morphisms. -/ -@[reassoc (attr := simp)] +@[to_dual none, reassoc (attr := simp)] theorem eqToHom_naturality {f g : β → C} (z : ∀ b, f b ⟶ g b) {j j' : β} (w : j = j') : z j ≫ eqToHom (by simp [w]) = eqToHom (by simp [w]) ≫ z j' := by cases w simp /-- A variant on `eqToHom_naturality` that helps Lean identify the families `f` and `g`. -/ -@[reassoc (attr := simp)] +@[to_dual none, reassoc (attr := simp)] theorem eqToHom_iso_hom_naturality {f g : β → C} (z : ∀ b, f b ≅ g b) {j j' : β} (w : j = j') : (z j).hom ≫ eqToHom (by simp [w]) = eqToHom (by simp [w]) ≫ (z j').hom := by cases w simp /-- A variant on `eqToHom_naturality` that helps Lean identify the families `f` and `g`. -/ -@[reassoc (attr := simp)] +@[to_dual none, reassoc (attr := simp)] theorem eqToHom_iso_inv_naturality {f g : β → C} (z : ∀ b, f b ≅ g b) {j j' : β} (w : j = j') : (z j).inv ≫ eqToHom (by simp [w]) = eqToHom (by simp [w]) ≫ (z j').inv := by cases w simp /-- Reducible form of `congrArg_mpr_hom_left` -/ -@[simp] +@[simp, to_dual none] theorem congrArg_cast_hom_left {X Y Z : C} (p : X = Y) (q : Y ⟶ Z) : cast (congrArg (fun W : C => W ⟶ Z) p.symm) q = eqToHom p ≫ q := by cases p @@ -161,13 +180,14 @@ we can replace the resulting `_.mpr f` term by a composition with an `eqToHom`. It may be advisable to introduce any necessary `eqToHom` morphisms manually, rather than relying on this lemma firing. -/ +@[to_dual none] theorem congrArg_mpr_hom_left {X Y Z : C} (p : X = Y) (q : Y ⟶ Z) : (congrArg (fun W : C => W ⟶ Z) p).mpr q = eqToHom p ≫ q := by cases p simp /-- Reducible form of `congrArg_mpr_hom_right` -/ -@[simp] +@[simp, to_dual none] theorem congrArg_cast_hom_right {X Y Z : C} (p : X ⟶ Y) (q : Z = Y) : cast (congrArg (fun W : C => X ⟶ W) q.symm) p = p ≫ eqToHom q.symm := by cases q @@ -180,6 +200,7 @@ we can replace the resulting `_.mpr f` term by a composition with an `eqToHom`. It may be advisable to introduce any necessary `eqToHom` morphisms manually, rather than relying on this lemma firing. -/ +@[to_dual none] theorem congrArg_mpr_hom_right {X Y Z : C} (p : X ⟶ Y) (q : Z = Y) : (congrArg (fun W : C => X ⟶ W) q).mpr p = p ≫ eqToHom q.symm := by cases q @@ -197,7 +218,7 @@ def eqToIso {X Y : C} (p : X = Y) : X ≅ Y := theorem eqToIso.hom {X Y : C} (p : X = Y) : (eqToIso p).hom = eqToHom p := rfl -@[simp] +@[simp, to_dual existing hom] theorem eqToIso.inv {X Y : C} (p : X = Y) : (eqToIso p).inv = eqToHom p.symm := rfl @@ -209,21 +230,22 @@ theorem eqToIso_refl {X : C} (p : X = X) : eqToIso p = Iso.refl X := theorem eqToIso_trans {X Y Z : C} (p : X = Y) (q : Y = Z) : eqToIso p ≪≫ eqToIso q = eqToIso (p.trans q) := by ext; simp -@[simp] +@[simp, to_dual none] theorem eqToHom_op {X Y : C} (h : X = Y) : (eqToHom h).op = eqToHom (congr_arg op h.symm) := by cases h rfl -@[simp] +@[simp, to_dual none] theorem eqToHom_unop {X Y : Cᵒᵖ} (h : X = Y) : (eqToHom h).unop = eqToHom (congr_arg unop h.symm) := by cases h rfl +@[to_dual none] instance {X Y : C} (h : X = Y) : IsIso (eqToHom h) := (eqToIso h).isIso_hom -@[simp] +@[simp, to_dual none] theorem inv_eqToHom {X Y : C} (h : X = Y) : inv (eqToHom h) = eqToHom h.symm := by cat_disch @@ -233,6 +255,7 @@ namespace Functor /-- Proving equality between functors. This isn't an extensionality lemma, because usually you don't really want to do this. -/ +@[to_dual none] theorem ext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X) (h_map : ∀ X Y f, F.map f = eqToHom (h_obj X) ≫ G.map f ≫ eqToHom (h_obj Y).symm := by cat_disch) : @@ -246,6 +269,7 @@ theorem ext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X) funext X Y f simpa using h_map X Y f +@[to_dual none] 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) := by cat_disch) : F = G := Functor.ext hobj (fun X Y f => by @@ -253,6 +277,7 @@ lemma ext_of_iso {F G : C ⥤ D} (e : F ≅ G) (hobj : ∀ X, F.obj X = G.obj X) Category.assoc, eqToHom_trans, eqToHom_refl, Category.comp_id]) /-- Proving equality between functors using heterogeneous equality. -/ +@[to_dual none] theorem hext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X) (h_map : ∀ (X Y) (f : X ⟶ Y), F.map f ≍ G.map f) : F = G := Functor.ext h_obj fun _ _ f => (conj_eqToHom_iff_heq _ _ (h_obj _) (h_obj _)).2 <| h_map _ _ f @@ -260,7 +285,7 @@ theorem hext {F G : C ⥤ D} (h_obj : ∀ X, F.obj X = G.obj X) -- Using equalities between functors. theorem congr_obj {F G : C ⥤ D} (h : F = G) (X) : F.obj X = G.obj X := by rw [h] -@[reassoc] +@[to_dual none, reassoc] theorem congr_hom {F G : C ⥤ D} (h : F = G) {X Y} (f : X ⟶ Y) : F.map f = eqToHom (congr_obj h X) ≫ G.map f ≫ eqToHom (congr_obj h Y).symm := by subst h; simp @@ -314,10 +339,11 @@ e.g. the naturality of a natural transformation. In some files it may be appropriate to use `attribute [local simp] eqToHom_map`, however. -/ +@[to_dual none] theorem eqToHom_map (F : C ⥤ D) {X Y : C} (p : X = Y) : F.map (eqToHom p) = eqToHom (congr_arg F.obj p) := by cases p; simp -@[reassoc (attr := simp)] +@[to_dual none, reassoc (attr := simp)] theorem eqToHom_map_comp (F : C ⥤ D) {X Y Z : C} (p : X = Y) (q : Y = Z) : F.map (eqToHom p) ≫ F.map (eqToHom q) = F.map (eqToHom <| p.trans q) := by cat_disch @@ -330,18 +356,21 @@ theorem eqToIso_map (F : C ⥤ D) {X Y : C} (p : X = Y) : theorem eqToIso_map_trans (F : C ⥤ D) {X Y Z : C} (p : X = Y) (q : Y = Z) : F.mapIso (eqToIso p) ≪≫ F.mapIso (eqToIso q) = F.mapIso (eqToIso <| p.trans q) := by cat_disch -@[simp] +@[simp, to_dual none] theorem eqToHom_app {F G : C ⥤ D} (h : F = G) (X : C) : (eqToHom h : F ⟶ G).app X = eqToHom (Functor.congr_obj h X) := by subst h; rfl +@[to_dual none] theorem NatTrans.congr {F G : C ⥤ D} (α : F ⟶ G) {X Y : C} (h : X = Y) : α.app X = F.map (eqToHom h) ≫ α.app Y ≫ G.map (eqToHom h.symm) := by rw [α.naturality_assoc] simp [eqToHom_map] +@[to_dual none] theorem eq_conj_eqToHom {X Y : C} (f : X ⟶ Y) : f = eqToHom rfl ≫ f ≫ eqToHom rfl := by simp only [Category.id_comp, eqToHom_refl, Category.comp_id] +@[to_dual none] theorem dcongr_arg {ι : Type*} {F G : ι → C} (α : ∀ i, F i ⟶ G i) {i j : ι} (h : i = j) : α i = eqToHom (congr_arg F h) ≫ α j ≫ eqToHom (congr_arg G h.symm) := by subst h diff --git a/Mathlib/CategoryTheory/Equivalence.lean b/Mathlib/CategoryTheory/Equivalence.lean index ae7d7d853f69c6..977bf5da0969a4 100644 --- a/Mathlib/CategoryTheory/Equivalence.lean +++ b/Mathlib/CategoryTheory/Equivalence.lean @@ -214,7 +214,6 @@ def mkIso {e f : C ≌ D} (η : e.functor ≅ f.functor) : e ≅ f where hom := mkHom η.hom inv := mkHom η.inv -set_option linter.existingAttributeWarning false in attribute [to_dual existing mkIso_inv] mkIso_hom variable (C D) in diff --git a/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean b/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean index ed5734b16d5cd4..0f0df6df1fc83e 100644 --- a/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean +++ b/Mathlib/CategoryTheory/FiberedCategory/BasedCategory.lean @@ -36,8 +36,8 @@ open Functor Category NatTrans IsHomLift variable {𝒮 : Type u₁} [Category.{v₁} 𝒮] +set_option linter.checkUnivs false in /-- A based category over `𝒮` is a category `𝒳` together with a functor `p : 𝒳 ⥤ 𝒮`. -/ -@[nolint checkUnivs] structure BasedCategory (𝒮 : Type u₁) [Category.{v₁} 𝒮] where /-- The type of objects in a `BasedCategory` -/ obj : Type u₂ diff --git a/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean index 7907d6276d3caa..18d798ebab7cd3 100644 --- a/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean +++ b/Mathlib/CategoryTheory/FiberedCategory/HasFibers.lean @@ -58,11 +58,11 @@ open CategoryTheory Functor Category IsCartesian IsHomLift Fiber variable {𝒮 : Type u₁} {𝒳 : Type u₂} [Category.{v₁} 𝒮] [Category.{v₂} 𝒳] +set_option linter.checkUnivs false in /-- 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₃ diff --git a/Mathlib/CategoryTheory/Filtered/Final.lean b/Mathlib/CategoryTheory/Filtered/Final.lean index edd610db14229d..3a35baf057910e 100644 --- a/Mathlib/CategoryTheory/Filtered/Final.lean +++ b/Mathlib/CategoryTheory/Filtered/Final.lean @@ -92,6 +92,14 @@ theorem isCofiltered_costructuredArrow_of_isCofiltered_of_exists [IsCofilteredOr obtain ⟨c', t, ht⟩ := h₂ s.unop s'.unop exact ⟨op c', Quiver.Hom.op t, Quiver.Hom.unop_inj ht⟩ +theorem exists_eq_of_isCofiltered_costructuredArrow {d : D} + [IsCofiltered (CostructuredArrow F d)] {c₁ c₂ : C} + (s₁ : F.obj c₁ ⟶ d) (s₂ : F.obj c₂ ⟶ d) : + ∃ (c : C) (t₁ : c ⟶ c₁) (t₂ : c ⟶ c₂), F.map t₁ ≫ s₁ = F.map t₂ ≫ s₂ := by + obtain ⟨W, p₁, p₂, -⟩ := IsCofilteredOrEmpty.cone_objs + (CostructuredArrow.mk s₁) (CostructuredArrow.mk s₂) + exact ⟨W.left, p₁.left, p₂.left, (CostructuredArrow.w p₁).trans (CostructuredArrow.w p₂).symm⟩ + /-- If `C` is filtered, then we can give an explicit condition for a functor `F : C ⥤ D` to be final. The converse is also true, see `final_iff_of_isFiltered`. -/ theorem Functor.final_of_exists_of_isFiltered [IsFilteredOrEmpty C] @@ -482,3 +490,19 @@ lemma Monotone.final_functor_iff {J₁ J₂ : Type*} [Preorder J₁] [Preorder J exact ⟨j₁, ⟨homOfLE h₁⟩⟩ · intro _ c _ _ exact ⟨c, 𝟙 _, rfl⟩ + +lemma Monotone.initial_functor_iff {J₁ J₂ : Type*} [Preorder J₁] [Preorder J₂] + [IsCodirectedOrder J₁] {f : J₁ → J₂} (hf : Monotone f) : + hf.functor.Initial ↔ ( ∀ j₁,∃ j₂, f j₂ ≤ j₁) := by + rw [Functor.initial_iff_of_isCofiltered] + constructor + · rintro ⟨h, _⟩ j₂ + obtain ⟨j₁, ⟨φ⟩⟩ := h j₂ + exact ⟨j₁,leOfHom φ⟩ + · intro h + constructor + · intro j₂ + obtain ⟨j₁, h₁⟩ := h j₂ + exact ⟨j₁, ⟨homOfLE h₁⟩⟩ + · intro _ c _ _ + exact ⟨ c, 𝟙 _, rfl⟩ diff --git a/Mathlib/CategoryTheory/Functor/Basic.lean b/Mathlib/CategoryTheory/Functor/Basic.lean index a17461442eee7e..9031718383e63f 100644 --- a/Mathlib/CategoryTheory/Functor/Basic.lean +++ b/Mathlib/CategoryTheory/Functor/Basic.lean @@ -58,6 +58,7 @@ attribute [grind =] Functor.map_id attribute [grind _=_] Functor.map_comp attribute [to_dual self] Functor.map Functor.map_comp attribute [to_dual self (reorder := map (X Y), map_comp (X Z, f g))] Functor.mk +attribute [to_dual self (reorder := mk (map (X Y), map_comp (X Z, f g)))] Functor.casesOn -- Note: We manually add this lemma which could be generated by `reassoc`, -- since we will import this file into `Mathlib/Tactic/CategoryTheory/Reassoc.lean`. diff --git a/Mathlib/CategoryTheory/Functor/Const.lean b/Mathlib/CategoryTheory/Functor/Const.lean index 970b034f3908e7..bda33f17f1d728 100644 --- a/Mathlib/CategoryTheory/Functor/Const.lean +++ b/Mathlib/CategoryTheory/Functor/Const.lean @@ -39,6 +39,8 @@ def const : C ⥤ J ⥤ C where map := fun _ => 𝟙 X } map f := { app := fun _ => f } +attribute [to_dual self] const_obj_map + namespace const open Opposite diff --git a/Mathlib/CategoryTheory/Generator/Basic.lean b/Mathlib/CategoryTheory/Generator/Basic.lean index 29ee725fc88105..64fa142dd8cc16 100644 --- a/Mathlib/CategoryTheory/Generator/Basic.lean +++ b/Mathlib/CategoryTheory/Generator/Basic.lean @@ -550,7 +550,7 @@ theorem IsSeparator.of_equivalence {G : C} (h : IsSeparator G) (α : C ≌ D) : theorem IsCoseparator.of_equivalence {G : C} (h : IsCoseparator G) (α : C ≌ D) : IsCoseparator (α.functor.obj G) := by - simpa using! ObjectProperty.IsCoseparating.of_equivalence h α + simpa using! ObjectProperty.IsCoseparating.of_equivalence h α end Equivalence diff --git a/Mathlib/CategoryTheory/GradedObject/Monoidal.lean b/Mathlib/CategoryTheory/GradedObject/Monoidal.lean index f76ec435b1c719..a00bb81d325d9a 100644 --- a/Mathlib/CategoryTheory/GradedObject/Monoidal.lean +++ b/Mathlib/CategoryTheory/GradedObject/Monoidal.lean @@ -326,7 +326,7 @@ abbrev _root_.CategoryTheory.GradedObject.HasLeftTensor₃ObjExt (j : I) := Pres (Discrete.functor fun (i : { i : (I × I × I) | i.1 + i.2.1 + i.2.2 = j }) ↦ (((mapTrifunctor (bifunctorComp₂₃ (curriedTensor C) (curriedTensor C)) I I I).obj X₁).obj X₂).obj X₃ i) - ((curriedTensor C).obj Z) + ((curriedTensor C).obj Z) variable {X₁ X₂ X₃} variable [HasTensor X₂ X₃] [HasTensor X₁ (tensorObj X₂ X₃)] @@ -610,8 +610,8 @@ instance (n : ℕ) : Finite ({ i : (ℕ × ℕ × ℕ) | i.1 + i.2.1 + i.2.2 = n refine Finite.of_injective (fun ⟨⟨i₁, i₂, i₃⟩, (hi : i₁ + i₂ + i₃ = n)⟩ => (⟨⟨i₁, by lia⟩, ⟨i₂, by lia⟩, ⟨i₃, by lia⟩⟩ : Fin (n + 1) × Fin (n + 1) × Fin (n + 1))) ?_ - rintro ⟨⟨_, _, _⟩, _⟩ ⟨⟨_, _, _⟩, _⟩ h - simpa using h + intro _ _ h + exact Subtype.ext (congrArg (fun x => (x.1.1, x.2.1.1, x.2.2.1)) h) /-! The monoidal category structure on `GradedObject ℕ C` can be inferred diff --git a/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean b/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean index 9674e4558b323d..fcfc708418d719 100644 --- a/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean +++ b/Mathlib/CategoryTheory/Groupoid/Grpd/Basic.lean @@ -33,8 +33,8 @@ universe v u namespace CategoryTheory -- intended to be used with explicit universe parameters +set_option linter.checkUnivs false in /-- Category of groupoids -/ -@[nolint checkUnivs] def Grpd := Bundled Groupoid.{v, u} diff --git a/Mathlib/CategoryTheory/Groupoid/VertexGroup.lean b/Mathlib/CategoryTheory/Groupoid/VertexGroup.lean index 289facce1b9efe..df6fd2c21367e8 100644 --- a/Mathlib/CategoryTheory/Groupoid/VertexGroup.lean +++ b/Mathlib/CategoryTheory/Groupoid/VertexGroup.lean @@ -83,9 +83,11 @@ def _root_.CategoryTheory.Functor.mapVertexGroup {D : Type v} [Groupoid D] (φ : map_one' := φ.map_id c map_mul' := φ.map_comp +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias CategoryTheory.Functor.mapVertexGroup := CategoryTheory.Functor.mapVertexGroup +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias CategoryTheory.Functor.mapVertexGroup_apply := CategoryTheory.Functor.mapVertexGroup_apply diff --git a/Mathlib/CategoryTheory/GuitartExact/KanExtension.lean b/Mathlib/CategoryTheory/GuitartExact/KanExtension.lean index 6c2f104be25efd..6237c66e5d64f0 100644 --- a/Mathlib/CategoryTheory/GuitartExact/KanExtension.lean +++ b/Mathlib/CategoryTheory/GuitartExact/KanExtension.lean @@ -118,7 +118,7 @@ end Functor.LeftExtension namespace TwoSquare variable {T : C₁ ⥤ C₂} {L : C₁ ⥤ C₃} {R : C₂ ⥤ C₄} {B : C₃ ⥤ C₄} - (w : TwoSquare T L R B) + (w : TwoSquare T L R B) include w diff --git a/Mathlib/CategoryTheory/IsConnected.lean b/Mathlib/CategoryTheory/IsConnected.lean index 2da559ece4f308..9ba6de15ee9e52 100644 --- a/Mathlib/CategoryTheory/IsConnected.lean +++ b/Mathlib/CategoryTheory/IsConnected.lean @@ -381,7 +381,7 @@ def Zigzag.setoid (J : Type u₂) [Category.{v₁} J] : Setoid J where -/ theorem zigzag_prefunctor_obj_of_zigzag (F : J ⥤q K) {j₁ j₂ : J} (h : Zigzag j₁ j₂) : Zigzag (F.obj j₁) (F.obj j₂) := - h.lift _ fun _ _ => Or.imp (Nonempty.map fun f => F.map f) (Nonempty.map fun f => F.map f) + h.lift F.obj fun _ _ => Or.imp (Nonempty.map fun f => F.map f) (Nonempty.map fun f => F.map f) /-- If there is a zigzag from `j₁` to `j₂`, then there is a zigzag from `F j₁` to `F j₂` as long as `F` is a functor. diff --git a/Mathlib/CategoryTheory/Iso.lean b/Mathlib/CategoryTheory/Iso.lean index 0807fc2e94279c..e2018dc0a520f5 100644 --- a/Mathlib/CategoryTheory/Iso.lean +++ b/Mathlib/CategoryTheory/Iso.lean @@ -117,7 +117,6 @@ def refl (X : C) : X ≅ X where hom := 𝟙 X inv := 𝟙 X -set_option linter.existingAttributeWarning false in attribute [to_dual existing refl_inv] refl_hom instance : Inhabited (X ≅ X) := ⟨Iso.refl X⟩ @@ -133,7 +132,6 @@ def trans (α : X ≅ Y) (β : Y ≅ Z) : X ≅ Z where hom := α.hom ≫ β.hom inv := β.inv ≫ α.inv -set_option linter.existingAttributeWarning false in attribute [to_dual existing trans_inv] trans_hom @[simps] @@ -393,6 +391,9 @@ theorem isIso_of_hom_comp_eq_id (g : X ⟶ Y) [IsIso g] {f : Y ⟶ X} (h : g ≫ rw [(hom_comp_eq_id _).mp h] infer_instance +lemma isIso_iff_of_thin [Quiver.IsThin C] {X Y : C} (f : X ⟶ Y) : IsIso f ↔ Nonempty (Y ⟶ X) := + ⟨fun _ ↦ ⟨inv f⟩, fun g ↦ ⟨g.some, Subsingleton.elim _ _, Subsingleton.elim _ _⟩⟩ + namespace Iso @[aesop apply safe (rule_sets := [CategoryTheory]), to_dual none] @@ -473,7 +474,6 @@ def mapIso (F : C ⥤ D) {X Y : C} (i : X ≅ Y) : F.obj X ≅ F.obj Y where hom := F.map i.hom inv := F.map i.inv -set_option linter.existingAttributeWarning false in attribute [to_dual existing mapIso_inv] mapIso_hom @[simp] diff --git a/Mathlib/CategoryTheory/LiftingProperties/Basic.lean b/Mathlib/CategoryTheory/LiftingProperties/Basic.lean index dbb84330753cbc..185a3dd028503c 100644 --- a/Mathlib/CategoryTheory/LiftingProperties/Basic.lean +++ b/Mathlib/CategoryTheory/LiftingProperties/Basic.lean @@ -36,6 +36,8 @@ open Category variable {C : Type*} [Category* C] {A B B' X Y Y' : C} (i : A ⟶ B) (i' : B ⟶ B') (p : X ⟶ Y) (p' : Y ⟶ Y') +to_dual_name_hint Left Right, A Y, B X, I P + /-- `HasLiftingProperty i p` means that `i` has the left lifting property with respect to `p`, or equivalently that `p` has the right lifting property with respect to `i`. -/ @@ -80,7 +82,7 @@ theorem iff_unop {A B X Y : Cᵒᵖ} (i : A ⟶ B) (p : X ⟶ Y) : variable (i p) -@[to_dual of_right_iso] +@[to_dual] instance (priority := 100) of_left_iso [IsIso i] : HasLiftingProperty i p := ⟨fun {f} {g} sq => CommSq.HasLift.mk' @@ -88,7 +90,7 @@ instance (priority := 100) of_left_iso [IsIso i] : HasLiftingProperty i p := fac_left := by simp only [IsIso.hom_inv_id_assoc] fac_right := by simp only [sq.w, assoc, IsIso.inv_hom_id_assoc] }⟩ -@[to_dual of_comp_right] +@[to_dual] instance of_comp_left [HasLiftingProperty i p] [HasLiftingProperty i' p] : HasLiftingProperty (i ≫ i') p := ⟨fun {f} {g} sq => by @@ -101,52 +103,30 @@ instance of_comp_left [HasLiftingProperty i p] [HasLiftingProperty i' p] : fac_right := by simp only [CommSq.fac_right] }⟩ set_option backward.isDefEq.respectTransparency false in +@[to_dual (reorder := i i' e p)] theorem of_arrow_iso_left {A B A' B' X Y : C} {i : A ⟶ B} {i' : A' ⟶ B'} (e : Arrow.mk i ≅ Arrow.mk i') (p : X ⟶ Y) [hip : HasLiftingProperty i p] : HasLiftingProperty i' p := by rw [Arrow.iso_w' e] infer_instance -set_option backward.isDefEq.respectTransparency false in -theorem of_arrow_iso_right {A B X Y X' Y' : C} (i : A ⟶ B) {p : X ⟶ Y} {p' : X' ⟶ Y'} - (e : Arrow.mk p ≅ Arrow.mk p') [hip : HasLiftingProperty i p] : HasLiftingProperty i p' := by - rw [Arrow.iso_w' e] - infer_instance - +@[to_dual (reorder := i i' e p)] theorem iff_of_arrow_iso_left {A B A' B' X Y : C} {i : A ⟶ B} {i' : A' ⟶ B'} (e : Arrow.mk i ≅ Arrow.mk i') (p : X ⟶ Y) : HasLiftingProperty i p ↔ HasLiftingProperty i' p := by constructor <;> intro exacts [of_arrow_iso_left e p, of_arrow_iso_left e.symm p] -theorem iff_of_arrow_iso_right {A B X Y X' Y' : C} (i : A ⟶ B) {p : X ⟶ Y} {p' : X' ⟶ Y'} - (e : Arrow.mk p ≅ Arrow.mk p') : HasLiftingProperty i p ↔ HasLiftingProperty i p' := by - constructor <;> intro - exacts [of_arrow_iso_right i e, of_arrow_iso_right i e.symm] - end HasLiftingProperty -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in -lemma RetractArrow.leftLiftingProperty - {X Y Z W Z' W' : C} {g : Z ⟶ W} {g' : Z' ⟶ W'} - (h : RetractArrow g' g) (f : X ⟶ Y) [HasLiftingProperty g f] : HasLiftingProperty g' f where - sq_hasLift := fun {u v} sq ↦ by - have sq' : CommSq (h.r.left ≫ u) g f (h.r.right ≫ v) := by simp only [Arrow.mk_left, - Arrow.mk_right, Category.assoc, sq.w, Arrow.w_mk_right_assoc, Arrow.mk_hom, CommSq.mk] - exact - ⟨⟨{ l := h.i.right ≫ sq'.lift - fac_left := by - simp only [← h.i_w_assoc, sq'.fac_left, h.retract_left_assoc, - Arrow.mk_left, Category.id_comp]}⟩⟩ - set_option backward.isDefEq.respectTransparency false in +@[to_dual] lemma RetractArrow.rightLiftingProperty {X Y Z W X' Y' : C} {f : X ⟶ Y} {f' : X' ⟶ Y'} (h : RetractArrow f' f) (g : Z ⟶ W) [HasLiftingProperty g f] : HasLiftingProperty g f' where sq_hasLift := fun {u v} sq ↦ have sq' : CommSq (u ≫ h.i.left) g f (v ≫ h.i.right) := - ⟨by rw [← Category.assoc, ← sq.w, Category.assoc, RetractArrow.i_w, Category.assoc]⟩ + ⟨by rw [← sq.w_assoc, Category.assoc, RetractArrow.i_w]⟩ ⟨⟨{ l := sq'.lift ≫ h.r.left}⟩⟩ namespace Arrow @@ -154,9 +134,11 @@ 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 `φ`. -/ +@[to_dual self] abbrev LiftStruct {f g : Arrow C} (φ : f ⟶ g) := (CommSq.mk φ.w).LiftStruct set_option backward.isDefEq.respectTransparency false in +@[to_dual self] 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 @@ -172,7 +154,7 @@ end Arrow /-- Given morphisms `i : A ⟶ B`, `p : X ⟶ Y`, `t : A ⟶ X`, this is the property that a lifting exists for all squares with `i` on left, `p` on the right and `t` on the top. -/ -@[to_dual (rename := i ↔ p, t → b, A ↔ Y, B ↔ X) (reorder := i p) +@[to_dual (rename := t → b) (reorder := i p) /-- Given morphisms `i : A ⟶ B`, `p : X ⟶ Y`, `b : B ⟶ Y`, this is the property that a lifting exists for all squares with `i` on left, `p` on the right and `b` on the bottom. -/] diff --git a/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean b/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean index 6be256b72cd9bc..6f6fecc53e31cd 100644 --- a/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean +++ b/Mathlib/CategoryTheory/LiftingProperties/PushoutProduct.lean @@ -68,7 +68,7 @@ lemma hasLiftingProperty_mk_iff' [HasPushouts C] [HasPullbacks C] set_option backward.defeqAttrib.useBackward true in /-- `(∅ ⟶ B) □ g` lifts against `X ⟶ Y` if and only if `g` lifts against `B ⟹ X ⟶ B ⟹ Y`. -/ -lemma hasLiftingProperty_mk_isInitial_iff [HasPushouts C] [HasPullbacks C] +lemma hasLiftingProperty_mk_isInitial_iff [HasPushouts C] [CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C] {A B K L X Y : C} {g : K ⟶ L} {h : X ⟶ Y} (i : IsInitial A) : @@ -80,7 +80,7 @@ lemma hasLiftingProperty_mk_isInitial_iff [HasPushouts C] [HasPullbacks C] exact Adjunction.hasLiftingProperty_iff (ihom.adjunction B) g h /-- `f □ (∅ ⟶ L)` lifts against `X ⟶ Y` if and only if `f` lifts against `L ⟹ X ⟶ L ⟹ Y`. -/ -lemma hasLiftingProperty_mk_isInitial_iff' [HasPushouts C] [HasPullbacks C] +lemma hasLiftingProperty_mk_isInitial_iff' [HasPushouts C] [CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C] {A B K L X Y : C} {f : A ⟶ B} {h : X ⟶ Y} (i : IsInitial K) : @@ -100,7 +100,7 @@ lemma hasLiftingProperty_mk_isTerminal_iff [HasPushouts C] [HasPullbacks C] exact HasLiftingProperty.iff_of_arrow_iso_right g (PullbackHom.isTerminalIso _ t) /-- `(∅ ⟶ B) □ g` lifts against `X ⟶ ⋆` if and only if `g` lifts against `(B ⟹ X) ⟶ ⋆`. -/ -lemma hasLiftingProperty_mk_isInitial_isTerminal_iff [HasPushouts C] [HasPullbacks C] +lemma hasLiftingProperty_mk_isInitial_isTerminal_iff [HasPushouts C] [CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C] {A B K L X Y : C} {g : K ⟶ L} (i : IsInitial A) (t : IsTerminal Y) : @@ -112,7 +112,7 @@ lemma hasLiftingProperty_mk_isInitial_isTerminal_iff [HasPushouts C] [HasPullbac (t.hom_ext _ _)) /-- `f □ (∅ ⟶ L)` lifts against `X ⟶ ⋆` if and only if `f` lifts against `(L ⟹ X) ⟶ ⋆`. -/ -lemma hasLiftingProperty_mk_isInitial_isTerminal_iff' [HasPushouts C] [HasPullbacks C] +lemma hasLiftingProperty_mk_isInitial_isTerminal_iff' [HasPushouts C] [CartesianMonoidalCategory C] [MonoidalClosed C] [BraidedCategory C] {A B K L X Y : C} {f : A ⟶ B} (i : IsInitial K) (t : IsTerminal Y) : diff --git a/Mathlib/CategoryTheory/Limits/Chosen/End.lean b/Mathlib/CategoryTheory/Limits/Chosen/End.lean index 29a4f819429c10..6e01480689f9b9 100644 --- a/Mathlib/CategoryTheory/Limits/Chosen/End.lean +++ b/Mathlib/CategoryTheory/Limits/Chosen/End.lean @@ -30,8 +30,9 @@ class ChosenCoendsOfShape (J : Type*) [Category* J] (C : Type*) [Category* C] wh /-- The chosen cowedge is colimiting. -/ isCoend (F : Jᵒᵖ ⥤ J ⥤ C) : IsColimit (cowedge F) +set_option linter.checkUnivs false in /-- The data of chosen coends in `C`. -/ -@[nolint checkUnivs, pp_with_univ] +@[pp_with_univ] abbrev ChosenCoends (C : Type*) [Category* C] := ∀ {J : Type u} [Category.{v} J], ChosenCoendsOfShape J C @@ -104,8 +105,9 @@ class ChosenEndsOfShape (J : Type*) [Category* J] (C : Type*) [Category* C] wher /-- The chosen wedge is limiting. -/ isEnd (F : Jᵒᵖ ⥤ J ⥤ C) : IsLimit (wedge F) +set_option linter.checkUnivs false in /-- The data of chosen ends in `C`. -/ -@[nolint checkUnivs, pp_with_univ] +@[pp_with_univ] abbrev ChosenEnds (C : Type*) [Category* C] := ∀ {J : Type u} [Category.{v} J], ChosenEndsOfShape J C diff --git a/Mathlib/CategoryTheory/Limits/Comma.lean b/Mathlib/CategoryTheory/Limits/Comma.lean index 8d0549e47beef3..31172ac7aeb408 100644 --- a/Mathlib/CategoryTheory/Limits/Comma.lean +++ b/Mathlib/CategoryTheory/Limits/Comma.lean @@ -10,6 +10,8 @@ public import Mathlib.CategoryTheory.Comma.Over.Basic public import Mathlib.CategoryTheory.Limits.Constructions.EpiMono public import Mathlib.CategoryTheory.Limits.Creates public import Mathlib.CategoryTheory.Limits.Unit +public import Mathlib.CategoryTheory.Limits.Preserves.Finite +public import Mathlib.CategoryTheory.Limits.Preserves.Creates.Finite /-! # Limits and colimits in comma categories @@ -150,6 +152,10 @@ instance hasLimitsOfSize [HasLimitsOfSize.{w, w'} A] [HasLimitsOfSize.{w, w'} B] [PreservesLimitsOfSize.{w, w'} R] : HasLimitsOfSize.{w, w'} (Comma L R) := ⟨fun _ _ => inferInstance⟩ +instance hasFiniteLimits [HasFiniteLimits A] [HasFiniteLimits B] + [PreservesFiniteLimits R] : HasFiniteLimits (Comma L R) where + out _ _ _ := inferInstance + instance hasColimit (F : J ⥤ Comma L R) [HasColimit (F ⋙ fst L R)] [HasColimit (F ⋙ snd L R)] [PreservesColimit (F ⋙ fst L R) L] : HasColimit F := HasColimit.mk ⟨_, coconeOfPreservesIsColimit _ (colimit.isColimit _) (colimit.isColimit _)⟩ @@ -161,6 +167,10 @@ instance hasColimitsOfSize [HasColimitsOfSize.{w, w'} A] [HasColimitsOfSize.{w, [PreservesColimitsOfSize.{w, w'} L] : HasColimitsOfSize.{w, w'} (Comma L R) := ⟨fun _ _ => inferInstance⟩ +instance hasFiniteColimits [HasFiniteColimits A] [HasFiniteColimits B] + [PreservesFiniteColimits L] : HasFiniteColimits (Comma L R) where + out _ _ _ := inferInstance + instance preservesColimitsOfShape_fst [HasColimitsOfShape J A] [HasColimitsOfShape J B] [PreservesColimitsOfShape J L] : PreservesColimitsOfShape J (Comma.fst L R) where preservesColimit := @@ -188,6 +198,9 @@ instance hasLimit (F : J ⥤ Arrow T) [i₁ : HasLimit (F ⋙ leftFunc)] [i₂ : instance hasLimitsOfShape [HasLimitsOfShape J T] : HasLimitsOfShape J (Arrow T) where +instance hasFiniteLimits [HasFiniteLimits T] : HasFiniteLimits (Arrow T) where + out _ _ _ := inferInstance + instance hasLimits [HasLimits T] : HasLimits (Arrow T) := ⟨fun _ _ => inferInstance⟩ @@ -200,6 +213,9 @@ instance hasColimit (F : J ⥤ Arrow T) [i₁ : HasColimit (F ⋙ leftFunc)] instance hasColimitsOfShape [HasColimitsOfShape J T] : HasColimitsOfShape J (Arrow T) where +instance hasFiniteColimits [HasFiniteColimits T] : HasFiniteColimits (Arrow T) where + out _ _ _ := inferInstance + instance hasColimits [HasColimits T] : HasColimits (Arrow T) := ⟨fun _ _ => inferInstance⟩ @@ -230,6 +246,10 @@ instance hasLimit [i₁ : HasLimit (F ⋙ proj X G)] [i₂ : PreservesLimit (F instance hasLimitsOfShape [HasLimitsOfShape J A] [PreservesLimitsOfShape J G] : HasLimitsOfShape J (StructuredArrow X G) where +instance hasFiniteLimits [HasFiniteLimits A] [PreservesFiniteLimits G] : + HasFiniteLimits (StructuredArrow X G) where + out _ _ _ := inferInstance + instance hasLimitsOfSize [HasLimitsOfSize.{w, w'} A] [PreservesLimitsOfSize.{w, w'} G] : HasLimitsOfSize.{w, w'} (StructuredArrow X G) := ⟨fun J hJ => by infer_instance⟩ @@ -246,6 +266,10 @@ noncomputable instance createsLimit [i : PreservesLimit (F ⋙ proj X G) G] : noncomputable instance createsLimitsOfShape [PreservesLimitsOfShape J G] : CreatesLimitsOfShape J (proj X G) where +noncomputable instance createsFiniteLimits [PreservesFiniteLimits G] : + CreatesFiniteLimits (proj X G) where + createsFiniteLimits _ _ _ := inferInstance + noncomputable instance createsLimitsOfSize [PreservesLimitsOfSize.{w, w'} G] : CreatesLimitsOfSize.{w, w'} (proj X G :) where @@ -277,6 +301,10 @@ instance hasColimit [i₁ : HasColimit (F ⋙ proj G X)] [i₂ : PreservesColimi instance hasColimitsOfShape [HasColimitsOfShape J A] [PreservesColimitsOfShape J G] : HasColimitsOfShape J (CostructuredArrow G X) where +instance hasFiniteColimits [HasFiniteColimits A] [PreservesFiniteColimits G] : + HasFiniteColimits (CostructuredArrow G X) where + out _ _ _ := inferInstance + instance hasColimitsOfSize [HasColimitsOfSize.{w, w'} A] [PreservesColimitsOfSize.{w, w'} G] : HasColimitsOfSize.{w, w'} (CostructuredArrow G X) := ⟨fun _ _ => inferInstance⟩ @@ -293,6 +321,10 @@ noncomputable instance createsColimit [i : PreservesColimit (F ⋙ proj G X) G] noncomputable instance createsColimitsOfShape [PreservesColimitsOfShape J G] : CreatesColimitsOfShape J (proj G X) where +noncomputable instance createsFiniteColimits [PreservesFiniteColimits G] : + CreatesFiniteColimits (proj G X) where + createsFiniteColimits _ _ _ := inferInstance + noncomputable instance createsColimitsOfSize [PreservesColimitsOfSize.{w, w'} G] : CreatesColimitsOfSize.{w, w'} (proj G X :) where diff --git a/Mathlib/CategoryTheory/Limits/Cones.lean b/Mathlib/CategoryTheory/Limits/Cones.lean index 5913294e8a5ba4..0d406a66ca30b3 100644 --- a/Mathlib/CategoryTheory/Limits/Cones.lean +++ b/Mathlib/CategoryTheory/Limits/Cones.lean @@ -151,13 +151,11 @@ instance inhabitedCone (F : Discrete PUnit ⥤ C) : Inhabited (Cone F) := }⟩ set_option backward.defeqAttrib.useBackward true in -@[to_dual (attr := reassoc (attr := simp))] +@[to_dual (attr := reassoc (attr := simp), elementwise)] theorem Cone.w {F : J ⥤ C} (c : Cone F) {j j' : J} (f : j ⟶ j') : dsimp% c.π.app j ≫ F.map f = c.π.app j' := by simpa using (c.π.naturality f).symm -attribute [elementwise] Cocone.w Cone.w - end variable {F : J ⥤ C} diff --git a/Mathlib/CategoryTheory/Limits/Creates.lean b/Mathlib/CategoryTheory/Limits/Creates.lean index 6e50771af6bbee..553b495bb93180 100644 --- a/Mathlib/CategoryTheory/Limits/Creates.lean +++ b/Mathlib/CategoryTheory/Limits/Creates.lean @@ -81,12 +81,13 @@ class CreatesLimitsOfShape (J : Type w) [Category.{w'} J] (F : C ⥤ D) where CreatesLimit : ∀ {K : J ⥤ C}, CreatesLimit K F := by infer_instance -- This should be used with explicit universe variables. +set_option linter.checkUnivs false in /-- `F` creates limits if it creates limits of shape `J` for any `J`. -/ -- After https://github.com/leanprover/lean4/pull/12286 and -- https://github.com/leanprover/lean4/pull/12423, the shape universes in -- `CreatesLimitsOfSize` and `CreatesColimitsOfSize` would default to universe output parameters. -- See Note [universe output parameters and typeclass caching]. -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class CreatesLimitsOfSize (F : C ⥤ D) where CreatesLimitsOfShape : ∀ {J : Type w} [Category.{w'} J], CreatesLimitsOfShape J F := by infer_instance @@ -114,8 +115,9 @@ class CreatesColimitsOfShape (J : Type w) [Category.{w'} J] (F : C ⥤ D) where CreatesColimit : ∀ {K : J ⥤ C}, CreatesColimit K F := by infer_instance -- This should be used with explicit universe variables. +set_option linter.checkUnivs false in /-- `F` creates colimits if it creates colimits of shape `J` for any small `J`. -/ -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class CreatesColimitsOfSize (F : C ⥤ D) where CreatesColimitsOfShape : ∀ {J : Type w} [Category.{w'} J], CreatesColimitsOfShape J F := by infer_instance diff --git a/Mathlib/CategoryTheory/Limits/FormalCoproducts/Basic.lean b/Mathlib/CategoryTheory/Limits/FormalCoproducts/Basic.lean index 6e237d4593500a..2c1324ea862275 100644 --- a/Mathlib/CategoryTheory/Limits/FormalCoproducts/Basic.lean +++ b/Mathlib/CategoryTheory/Limits/FormalCoproducts/Basic.lean @@ -240,6 +240,31 @@ lemma fromIncl_comp_cofanPtIsoSelf_inv (i : X.I) : Hom.fromIncl i (𝟙 (X.obj i)) ≫ (coproductIsoSelf X).inv = Sigma.ι X.toFun i := (Iso.comp_inv_eq _).2 (ι_comp_coproductIsoSelf_hom _ _).symm +/-- Given an object `X : FormalCoproduct C` and an equality of indices `i = j`, this is +the induced isomorphism `Xᵢ ≅ Xⱼ`. -/ +def objIsoOfEq (X : FormalCoproduct.{w} C) {i j : X.I} (hij : i = j) : + X.obj i ≅ X.obj j := + eqToIso (by rw [hij]) + +@[simp] +lemma objIsoOfEq_rfl (X : FormalCoproduct.{w} C) (i : X.I) : + X.objIsoOfEq (rfl : i = i) = Iso.refl _ := + rfl + +@[simp] +lemma objIsoOfEq_trans (X : FormalCoproduct.{w} C) {i j k : X.I} + (hij : i = j) (hjk : j = k) : + X.objIsoOfEq hij ≪≫ X.objIsoOfEq hjk = X.objIsoOfEq (hij.trans hjk) := by + subst hij hjk + simp + +@[simp] +lemma objIsoOfEq_symm (X : FormalCoproduct.{w} C) {i j : X.I} + (hij : i = j) : + (X.objIsoOfEq hij).symm = X.objIsoOfEq hij.symm := by + subst hij + simp + end Coproduct section Terminal @@ -375,6 +400,22 @@ instance : PreservesColimit (Discrete.functor f) ((eval.{w} C A).obj F) := instance : PreservesColimitsOfShape (Discrete J) ((eval.{w} C A).obj F) := preservesColimitsOfShape_of_discrete _ +/-- The yoneda embedding of `FormalCoproduct.{v} C` into `v`-presheaves. -/ +protected noncomputable abbrev yoneda : + FormalCoproduct.{v} C ⥤ Cᵒᵖ ⥤ Type v := + (eval _ _).obj yoneda + +/-- The yoneda embedding of `FormalCoproduct.{w} C` into `max w v`-presheaves. -/ +protected noncomputable abbrev uliftYoneda : + FormalCoproduct.{w} C ⥤ Cᵒᵖ ⥤ Type (max w v) := + (eval _ _).obj uliftYoneda + +/-- The yoneda embedding of `FormalCoproduct.{w} C` into `w`-presheaves for a locally +`w`-small category. -/ +protected noncomputable abbrev shrinkYoneda [LocallySmall.{w} C] : + FormalCoproduct.{w} C ⥤ Cᵒᵖ ⥤ Type w := + (eval _ _).obj shrinkYoneda + end HasCoproducts noncomputable section HasProducts diff --git a/Mathlib/CategoryTheory/Limits/Indization/Category.lean b/Mathlib/CategoryTheory/Limits/Indization/Category.lean index 8d05c3bbbb0845..30937a6d5a8a6a 100644 --- a/Mathlib/CategoryTheory/Limits/Indization/Category.lean +++ b/Mathlib/CategoryTheory/Limits/Indization/Category.lean @@ -5,6 +5,7 @@ Authors: Markus Himmel -/ module +public import Mathlib.CategoryTheory.Functor.Flat public import Mathlib.CategoryTheory.Limits.Constructions.Filtered public import Mathlib.CategoryTheory.Limits.FullSubcategory public import Mathlib.CategoryTheory.Limits.ExactFunctor diff --git a/Mathlib/CategoryTheory/Limits/IsLimit.lean b/Mathlib/CategoryTheory/Limits/IsLimit.lean index 3045f689aa7f55..57c4bedb521de0 100644 --- a/Mathlib/CategoryTheory/Limits/IsLimit.lean +++ b/Mathlib/CategoryTheory/Limits/IsLimit.lean @@ -61,43 +61,69 @@ structure IsLimit (t : Cone F) where uniq : ∀ (s : Cone F) (m : s.pt ⟶ t.pt) (_ : ∀ j : J, m ≫ t.π.app j = s.π.app j), m = lift s := by cat_disch -attribute [reassoc (attr := simp)] IsLimit.fac +set_option backward.defeqAttrib.useBackward true in +/-- A cocone `t` on `F` is a colimit cocone if each cocone on `F` admits a unique +cocone morphism from `t`. -/ +@[stacks 002F, to_dual] +structure IsColimit (t : Cocone F) where + /-- `t.pt` maps to all other cocone covertices -/ + desc : ∀ s : Cocone F, t.pt ⟶ s.pt + /-- The map `desc` makes the diagram with the natural transformations commute -/ + fac : ∀ (s : Cocone F) (j : J), dsimp% t.ι.app j ≫ desc s = s.ι.app j := by cat_disch + /-- `desc` is the unique such map -/ + uniq : + ∀ (s : Cocone F) (m : t.pt ⟶ s.pt) (_ : ∀ j : J, t.ι.app j ≫ m = s.ι.app j), m = desc s := by + cat_disch + +attribute [reassoc (attr := simp)] IsLimit.fac IsColimit.fac + +to_dual_name_hint Lift Desc, Left Right namespace IsLimit +@[to_dual] instance subsingleton {t : Cone F} : Subsingleton (IsLimit t) := ⟨by intro P Q; cases P; cases Q; congr; cat_disch⟩ /-- Given a natural transformation `α : F ⟶ G`, we give a morphism from the cone point of any cone over `F` to the cone point of a limit cone over `G`. -/ +@[to_dual (reorder := s P t) +/-- Given a natural transformation `α : F ⟶ G`, we give a morphism from the cocone point +of a colimit cocone over `F` to the cocone point of any cocone over `G`. -/] def map {F G : J ⥤ C} (s : Cone F) {t : Cone G} (P : IsLimit t) (α : F ⟶ G) : s.pt ⟶ t.pt := P.lift ((Cone.postcompose α).obj s) -@[reassoc (attr := simp)] +-- The `set_option` is needed to make reassoc generate the right theorem +set_option backward.isDefEq.respectTransparency false in +@[to_dual (attr := reassoc (attr := simp)) (reorder := c hd d) ι_map] theorem map_π {F G : J ⥤ C} (c : Cone F) {d : Cone G} (hd : IsLimit d) (α : F ⟶ G) (j : J) : hd.map c α ≫ d.π.app j = c.π.app j ≫ α.app j := fac _ _ _ -@[simp] +@[to_dual (attr := simp)] theorem lift_self {c : Cone F} (t : IsLimit c) : t.lift c = 𝟙 c.pt := (t.uniq _ _ fun _ => id_comp _).symm -- Repackaging the definition in terms of cone morphisms. /-- The universal morphism from any other cone to a limit cone. -/ -@[simps] +@[to_dual (attr := simps) +/-- The universal morphism from a colimit cocone to any other cocone. -/] def liftConeMorphism {t : Cone F} (h : IsLimit t) (s : Cone F) : s ⟶ t where hom := h.lift s +@[to_dual] theorem uniq_cone_morphism {s t : Cone F} (h : IsLimit t) {f f' : s ⟶ t} : f = f' := have : ∀ {g : s ⟶ t}, g = h.liftConeMorphism s := by intro g; apply ConeMorphism.ext; exact h.uniq _ _ g.w this.trans this.symm /-- Restating the definition of a limit cone in terms of the ∃! operator. -/ +@[to_dual /-- Restating the definition of a colimit cocone in terms of the ∃! operator. -/] theorem existsUnique {t : Cone F} (h : IsLimit t) (s : Cone F) : ∃! l : s.pt ⟶ t.pt, ∀ j, l ≫ t.π.app j = s.π.app j := ⟨h.lift s, h.fac s, h.uniq s⟩ /-- Noncomputably make a limit cone from the existence of unique factorizations. -/ +@[to_dual /-- Noncomputably make a colimit cocone from the existence of unique factorizations. -/] def ofExistsUnique {t : Cone F} (ht : ∀ s : Cone F, ∃! l : s.pt ⟶ t.pt, ∀ j, l ≫ t.π.app j = s.π.app j) : IsLimit t := by choose s hs hs' using ht @@ -107,7 +133,11 @@ def ofExistsUnique {t : Cone F} providing a morphism of cones rather than a morphism between the cone points and separately the factorisation condition. -/ -@[simps] +@[to_dual (attr := simps) +/-- Alternative constructor for `IsColimit`, +providing a morphism of cocones rather than a morphism between the cocone points +and separately the factorisation condition. +-/] def mkConeMorphism {t : Cone F} (lift : ∀ s : Cone F, s ⟶ t) (uniq : ∀ (s : Cone F) (m : s ⟶ t), m = lift s) : IsLimit t where lift s := (lift s).hom @@ -116,7 +146,7 @@ def mkConeMorphism {t : Cone F} (lift : ∀ s : Cone F, s ⟶ t) congrArg ConeMorphism.hom this /-- Limit cones on `F` are unique up to isomorphism. -/ -@[simps] +@[to_dual (attr := simps) /-- Colimit cocones on `F` are unique up to isomorphism. -/] def uniqueUpToIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : s ≅ t where hom := Q.liftConeMorphism s inv := P.liftConeMorphism t @@ -124,56 +154,61 @@ def uniqueUpToIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : s ≅ t where inv_hom_id := Q.uniq_cone_morphism /-- Any cone morphism between limit cones is an isomorphism. -/ +@[to_dual (reorder := P Q) /-- Any cocone morphism between colimit cocones is an isomorphism. -/] theorem hom_isIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) (f : s ⟶ t) : IsIso f := ⟨⟨P.liftConeMorphism t, ⟨P.uniq_cone_morphism, Q.uniq_cone_morphism⟩⟩⟩ /-- Limits of `F` are unique up to isomorphism. -/ +@[to_dual /-- Colimits of `F` are unique up to isomorphism. -/] def conePointUniqueUpToIso {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : s.pt ≅ t.pt := (Cone.forget F).mapIso (uniqueUpToIso P Q) -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp)) comp_coconePointUniqueUpToIso_inv] theorem conePointUniqueUpToIso_hom_comp {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) (j : J) : (conePointUniqueUpToIso P Q).hom ≫ t.π.app j = s.π.app j := (uniqueUpToIso P Q).hom.w _ -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp)) comp_coconePointUniqueUpToIso_hom] theorem conePointUniqueUpToIso_inv_comp {s t : Cone F} (P : IsLimit s) (Q : IsLimit t) (j : J) : (conePointUniqueUpToIso P Q).inv ≫ s.π.app j = t.π.app j := (uniqueUpToIso P Q).inv.w _ -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp)) coconePointUniqueUpToIso_inv_desc] theorem lift_comp_conePointUniqueUpToIso_hom {r s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : P.lift r ≫ (conePointUniqueUpToIso P Q).hom = Q.lift r := Q.uniq _ _ (by simp) -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp)) coconePointUniqueUpToIso_hom_desc] theorem lift_comp_conePointUniqueUpToIso_inv {r s t : Cone F} (P : IsLimit s) (Q : IsLimit t) : Q.lift r ≫ (conePointUniqueUpToIso P Q).inv = P.lift r := P.uniq _ _ (by simp) /-- Transport evidence that a cone is a limit cone across an isomorphism of cones. -/ +@[to_dual +/-- Transport evidence that a cocone is a colimit cocone across an isomorphism of cocones. -/] def ofIsoLimit {r t : Cone F} (P : IsLimit r) (i : r ≅ t) : IsLimit t := IsLimit.mkConeMorphism (fun s => P.liftConeMorphism s ≫ i.hom) fun s m => by rw [← i.comp_inv_eq]; apply P.uniq_cone_morphism -@[simp] +@[to_dual (attr := simp)] theorem ofIsoLimit_lift {r t : Cone F} (P : IsLimit r) (i : r ≅ t) (s) : (P.ofIsoLimit i).lift s = P.lift s ≫ i.hom.hom := rfl /-- Isomorphism of cones preserves whether or not they are limiting cones. -/ +@[to_dual /-- Isomorphism of cocones preserves whether or not they are colimiting cocones. -/] def equivIsoLimit {r t : Cone F} (i : r ≅ t) : IsLimit r ≃ IsLimit t where toFun h := h.ofIsoLimit i invFun h := h.ofIsoLimit i.symm left_inv := by cat_disch right_inv := by cat_disch -@[simp] +@[to_dual (attr := simp)] theorem equivIsoLimit_apply {r t : Cone F} (i : r ≅ t) (P : IsLimit r) : equivIsoLimit i P = P.ofIsoLimit i := rfl -@[simp] +@[to_dual (attr := simp)] theorem equivIsoLimit_symm_apply {r t : Cone F} (i : r ≅ t) (P : IsLimit t) : (equivIsoLimit i).symm P = P.ofIsoLimit i.symm := rfl @@ -181,6 +216,10 @@ theorem equivIsoLimit_symm_apply {r t : Cone F} (i : r ≅ t) (P : IsLimit t) : /-- If the canonical morphism from a cone point to a limiting cone point is an iso, then the first cone was limiting also. -/ +@[to_dual +/-- If the canonical morphism to a cocone point from a colimiting cocone point is an iso, then the +first cocone was colimiting also. +-/] def ofPointIso {r t : Cone F} (P : IsLimit r) [i : IsIso (P.lift t)] : IsLimit t := ofIsoLimit P (by haveI : IsIso (P.liftConeMorphism t).hom := i @@ -191,17 +230,21 @@ def ofPointIso {r t : Cone F} (P : IsLimit r) [i : IsIso (P.lift t)] : IsLimit t variable {t : Cone F} set_option backward.defeqAttrib.useBackward true in +@[to_dual] theorem hom_lift (h : IsLimit t) {W : C} (m : W ⟶ t.pt) : m = h.lift { pt := W, π := { app := fun b => m ≫ t.π.app b } } := h.uniq { pt := W, π := { app := fun b => m ≫ t.π.app b } } m fun _ => rfl /-- Two morphisms into a limit are equal if their compositions with - each cone morphism are equal. -/ +each cone morphism are equal. -/ +@[to_dual /-- Two morphisms out of a colimit are equal if their compositions with +each cocone morphism are equal. -/] theorem hom_ext (h : IsLimit t) {W : C} {f f' : W ⟶ t.pt} (w : ∀ j, f ≫ t.π.app j = f' ≫ t.π.app j) : f = f' := by rw [h.hom_lift f, h.hom_lift f']; congr; exact funext w +@[to_dual] lemma nonempty_isLimit_iff_isIso_lift {s t : Cone F} (hs : IsLimit s) : Nonempty (IsLimit t) ↔ IsIso (hs.lift t) := ⟨fun ⟨ht⟩ ↦ ⟨ht.lift s, ht.hom_ext (by simp), hs.hom_ext (by simp)⟩, fun h ↦ ⟨hs.ofPointIso⟩⟩ @@ -209,6 +252,10 @@ lemma nonempty_isLimit_iff_isIso_lift {s t : Cone F} (hs : IsLimit s) : /-- Given a right adjoint functor between categories of cones, the image of a limit cone is a limit cone. -/ +@[to_dual +/-- Given a left adjoint functor between categories of cocones, +the image of a colimit cocone is a colimit cocone. +-/] def ofRightAdjoint {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} {left : Cone F ⥤ Cone G} {right : Cone G ⥤ Cone F} (adj : left ⊣ right) {c : Cone G} (t : IsLimit c) : IsLimit (right.obj c) := @@ -225,31 +272,56 @@ def ofConeEquiv {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cone G left_inv := by cat_disch right_inv := by cat_disch -@[simp] -theorem ofConeEquiv_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cone G ≌ Cone F) +/-- Given two functors which have equivalent categories of cocones, +we can transport a colimiting cocone across the equivalence. +-/ +@[to_dual existing] +def _root_.CategoryTheory.Limits.IsColimit.ofCoconeEquiv {D : Type u₄} [Category.{v₄} D] + {G : K ⥤ D} (h : Cocone G ≌ Cocone F) {c : Cocone G} : + IsColimit (h.functor.obj c) ≃ IsColimit c where + toFun P := IsColimit.ofIsoColimit (IsColimit.ofLeftAdjoint h.symm.toAdjunction P) + (h.unitIso.symm.app c) + invFun := IsColimit.ofLeftAdjoint h.toAdjunction + left_inv := by cat_disch + right_inv := by cat_disch + +@[to_dual (attr := simp)] +theorem ofConeEquiv_apply_lift {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cone G ≌ Cone F) {c : Cone G} (P : IsLimit (h.functor.obj c)) (s) : (ofConeEquiv h P).lift s = ((h.unitIso.hom.app s).hom ≫ (h.inverse.map (P.liftConeMorphism (h.functor.obj s))).hom) ≫ (h.unitIso.inv.app c).hom := rfl -@[simp] -theorem ofConeEquiv_symm_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} +@[to_dual (attr := simp)] +theorem ofConeEquiv_symm_apply_lift {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cone G ≌ Cone F) {c : Cone G} (P : IsLimit c) (s) : ((ofConeEquiv h).symm P).lift s = (h.counitIso.inv.app s).hom ≫ (h.functor.map (P.liftConeMorphism (h.inverse.obj s))).hom := rfl -/-- -A cone postcomposed with a natural isomorphism is a limit cone if and only if the original cone is. +@[deprecated (since := "2026-06-21")] alias ofConeEquiv_apply_desc := ofConeEquiv_apply_lift +@[deprecated (since := "2026-06-21")] +alias ofConeEquiv_symm_apply_desc := ofConeEquiv_symm_apply_lift + +/-- A cone postcomposed with a natural isomorphism is a limit cone +if and only if the original cone is. -/ +@[to_dual precomposeInvEquiv +/-- A cocone precomposed with the inverse of a natural isomorphism is a colimit cocone +if and only if the original cocone is. +-/] def postcomposeHomEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cone F) : IsLimit ((Cone.postcompose α.hom).obj c) ≃ IsLimit c := ofConeEquiv (Cone.postcomposeEquivalence α) -/-- A cone postcomposed with the inverse of a natural isomorphism is a limit cone if and only if -the original cone is. +/-- A cone postcomposed with the inverse of a natural isomorphism is a limit cone +if and only if the original cone is. -/ +@[to_dual precomposeHomEquiv +/-- A cocone precomposed with a natural isomorphism is a colimit cocone +if and only if the original cocone is. +-/] def postcomposeInvEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cone G) : IsLimit ((Cone.postcompose α.inv).obj c) ≃ IsLimit c := postcomposeHomEquiv α.symm c @@ -257,6 +329,10 @@ def postcomposeInvEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cone G) : /-- Constructing an equivalence `IsLimit c ≃ IsLimit d` from a natural isomorphism between the underlying functors, and then an isomorphism between `c` transported along this and `d`. -/ +@[to_dual +/-- Constructing an equivalence `isColimit c ≃ isColimit d` from a natural isomorphism +between the underlying functors, and then an isomorphism between `c` transported along this and `d`. +-/] def equivOfNatIsoOfIso {F G : J ⥤ C} (α : F ≅ G) (c : Cone F) (d : Cone G) (w : (Cone.postcompose α.hom).obj c ≅ d) : IsLimit c ≃ IsLimit d := (postcomposeHomEquiv α _).symm.trans (equivIsoLimit w) @@ -265,7 +341,10 @@ set_option backward.defeqAttrib.useBackward true in /-- The cone points of two limit cones for naturally isomorphic functors are themselves isomorphic. -/ -@[simps] +@[to_dual (attr := simps) +/-- The cocone points of two colimit cocones for naturally isomorphic functors +are themselves isomorphic. +-/] def conePointsIsoOfNatIso {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit s) (Q : IsLimit t) (w : F ≅ G) : s.pt ≅ t.pt where hom := Q.map s w.hom @@ -273,25 +352,30 @@ def conePointsIsoOfNatIso {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit hom_inv_id := P.hom_ext (by simp) inv_hom_id := Q.hom_ext (by simp) -@[reassoc] +set_option linter.translateOverwrite false in +attribute [to_dual existing IsColimit.coconePointsIsoOfNatIso_inv] conePointsIsoOfNatIso_hom +set_option linter.translateOverwrite false in +attribute [to_dual existing IsColimit.coconePointsIsoOfNatIso_hom] conePointsIsoOfNatIso_inv + +@[to_dual (attr := reassoc) comp_coconePointsIsoOfNatIso_inv] theorem conePointsIsoOfNatIso_hom_comp {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit s) (Q : IsLimit t) (w : F ≅ G) (j : J) : (conePointsIsoOfNatIso P Q w).hom ≫ t.π.app j = s.π.app j ≫ w.hom.app j := by simp -@[reassoc] +@[to_dual (attr := reassoc) comp_coconePointsIsoOfNatIso_hom] theorem conePointsIsoOfNatIso_inv_comp {F G : J ⥤ C} {s : Cone F} {t : Cone G} (P : IsLimit s) (Q : IsLimit t) (w : F ≅ G) (j : J) : (conePointsIsoOfNatIso P Q w).inv ≫ s.π.app j = t.π.app j ≫ w.inv.app j := by simp set_option backward.defeqAttrib.useBackward true in -@[reassoc] +@[to_dual (attr := reassoc) coconePointsIsoOfNatIso_inv_desc] theorem lift_comp_conePointsIsoOfNatIso_hom {F G : J ⥤ C} {r s : Cone F} {t : Cone G} (P : IsLimit s) (Q : IsLimit t) (w : F ≅ G) : P.lift r ≫ (conePointsIsoOfNatIso P Q w).hom = Q.map r w.hom := Q.hom_ext (by simp) set_option backward.defeqAttrib.useBackward true in -@[reassoc] +@[to_dual (attr := reassoc) coconePointsIsoOfNatIso_hom_desc] theorem lift_comp_conePointsIsoOfNatIso_inv {F G : J ⥤ C} {r s : Cone G} {t : Cone F} (P : IsLimit t) (Q : IsLimit s) (w : F ≅ G) : Q.lift r ≫ (conePointsIsoOfNatIso P Q w).inv = P.map r w.inv := @@ -301,33 +385,48 @@ section Equivalence open CategoryTheory.Equivalence -/-- If `s : Cone F` is a limit cone, so is `s` whiskered by an equivalence `e`. --/ +/-- If `s : Cone F` is a limit cone, so is `s` whiskered by an equivalence `e`. -/ def whiskerEquivalence {s : Cone F} (P : IsLimit s) (e : K ≌ J) : IsLimit (s.whisker e.functor) := ofRightAdjoint (Cone.whiskeringEquivalence e).symm.toAdjunction P -/-- If `s : Cone F` whiskered by an equivalence `e` is a limit cone, so is `s`. --/ +/-- If `s : Cocone F` is a colimit cocone, so is `s` whiskered by an equivalence `e`. -/ +@[to_dual existing] +def _root_.CategoryTheory.Limits.IsColimit.whiskerEquivalence {s : Cocone F} + (P : IsColimit s) (e : K ≌ J) : IsColimit (s.whisker e.functor) := + IsColimit.ofLeftAdjoint (Cocone.whiskeringEquivalence e).toAdjunction P + +/-- If `s : Cone F` whiskered by an equivalence `e` is a limit cone, so is `s`. -/ def ofWhiskerEquivalence {s : Cone F} (e : K ≌ J) (P : IsLimit (s.whisker e.functor)) : IsLimit s := equivIsoLimit ((Cone.whiskeringEquivalence e).unitIso.app s).symm (ofRightAdjoint (Cone.whiskeringEquivalence e).toAdjunction P) -/-- Given an equivalence of diagrams `e`, `s` is a limit cone iff `s.whisker e.functor` is. --/ +/-- If `s : Cocone F` whiskered by an equivalence `e` is a colimit cocone, so is `s`. -/ +@[to_dual existing] +def _root_.CategoryTheory.Limits.IsColimit.ofWhiskerEquivalence {s : Cocone F} (e : K ≌ J) + (P : IsColimit (s.whisker e.functor)) : IsColimit s := + IsColimit.equivIsoColimit ((Cocone.whiskeringEquivalence e).unitIso.app s).symm + (IsColimit.ofLeftAdjoint (Cocone.whiskeringEquivalence e).symm.toAdjunction P) + +/-- Given an equivalence of diagrams `e`, `s` is a limit cone iff `s.whisker e.functor` is. -/ +@[to_dual +/-- Given an equivalence of diagrams `e`, `s` is a colimit cocone iff `s.whisker e.functor` is. -/] def whiskerEquivalenceEquiv {s : Cone F} (e : K ≌ J) : IsLimit s ≃ IsLimit (s.whisker e.functor) := ⟨fun h => h.whiskerEquivalence e, ofWhiskerEquivalence e, by cat_disch, by cat_disch⟩ /-- A limit cone extended by an isomorphism is a limit cone. -/ +@[to_dual /-- A colimit cocone extended by an isomorphism is a colimit cocone. -/] def extendIso {s : Cone F} {X : C} (i : X ⟶ s.pt) [IsIso i] (hs : IsLimit s) : IsLimit (s.extend i) := IsLimit.ofIsoLimit hs (Cone.extendIso s (asIso' i)) /-- A cone is a limit cone if its extension by an isomorphism is. -/ +@[to_dual /-- A cocone is a colimit cocone if its extension by an isomorphism is. -/] def ofExtendIso {s : Cone F} {X : C} (i : X ⟶ s.pt) [IsIso i] (hs : IsLimit (s.extend i)) : IsLimit s := IsLimit.ofIsoLimit hs (Cone.extendIso s (asIso' i)).symm /-- A cone is a limit cone iff its extension by an isomorphism is. -/ +@[to_dual /-- A cocone is a colimit cocone iff its extension by an isomorphism is. -/] def extendIsoEquiv {s : Cone F} {X : C} (i : X ⟶ s.pt) [IsIso i] : IsLimit s ≃ IsLimit (s.extend i) := equivOfSubsingletonOfSubsingleton (extendIso i) (ofExtendIso i) @@ -343,7 +442,16 @@ This is the most general form of uniqueness of cone points, allowing relabelling of both the indexing category (up to equivalence) and the functor (up to natural isomorphism). -/ -@[simps] +@[to_dual (attr := simps) +/-- We can prove two cocone points `(s : Cocone F).pt` and `(t : Cocone G).pt` are isomorphic if +* both cocones are colimit cocones +* their indexing categories are equivalent via some `e : J ≌ K`, +* the triangle of functors commutes up to a natural isomorphism: `e.functor ⋙ G ≅ F`. + +This is the most general form of uniqueness of cocone points, +allowing relabelling of both the indexing category (up to equivalence) +and the functor (up to natural isomorphism). +-/] def conePointsIsoOfEquivalence {F : J ⥤ C} {s : Cone F} {G : K ⥤ C} {t : Cone G} (P : IsLimit s) (Q : IsLimit t) (e : J ≌ K) (w : e.functor ⋙ G ≅ F) : s.pt ≅ t.pt := let w' : e.inverse ⋙ F ≅ G := (isoWhiskerLeft e.inverse w).symm ≪≫ invFunIdAssoc e G @@ -354,45 +462,55 @@ def conePointsIsoOfEquivalence {F : J ⥤ C} {s : Cone F} {G : K ⥤ C} {t : Con dsimp [w'] simp only [Limits.Cone.whisker_π, Limits.Cone.postcompose_obj_π, fac, whiskerLeft_app, assoc, id_comp, invFunIdAssoc_hom_app, fac_assoc, NatTrans.comp_app] - rw [counit_app_functor, ← Functor.comp_map] - have l : - NatTrans.app w.hom j = NatTrans.app w.hom ((𝟭 J).obj j) := by dsimp - rw [l, w.hom.naturality] + rw [counit_app_functor, ← Functor.comp_map, ← w.inv.naturality_assoc] simp inv_hom_id := by apply hom_ext Q cat_disch } +set_option linter.translateOverwrite false in +attribute [to_dual existing IsColimit.coconePointsIsoOfEquivalence_inv] + conePointsIsoOfEquivalence_hom +set_option linter.translateOverwrite false in +attribute [to_dual existing IsColimit.coconePointsIsoOfEquivalence_hom] + conePointsIsoOfEquivalence_inv + end Equivalence set_option backward.defeqAttrib.useBackward true in -/-- The universal property of a limit cone: a wap `W ⟶ t.pt` is the same as - a cone on `F` with cone point `W`. -/ -@[simps apply] +/-- The universal property of a limit cone: a map `W ⟶ t.pt` is the same as +a cone on `F` with cone point `W`. -/ +@[to_dual (attr := simps apply) +/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as +a cocone on `F` with cone point `W`. -/] def homEquiv (h : IsLimit t) {W : C} : (W ⟶ t.pt) ≃ ((Functor.const J).obj W ⟶ F) where toFun f := (t.extend f).π invFun π := h.lift (Cone.mk _ π) left_inv f := h.hom_ext (by simp) right_inv π := by cat_disch -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp)) ι_app_homEquiv_symm] lemma homEquiv_symm_π_app (h : IsLimit t) {W : C} (f : (const J).obj W ⟶ F) (j : J) : h.homEquiv.symm f ≫ t.π.app j = f.app j := by simp [homEquiv] set_option backward.defeqAttrib.useBackward true in +@[to_dual] lemma homEquiv_symm_naturality (h : IsLimit t) {W W' : C} (f : (const J).obj W ⟶ F) (g : W' ⟶ W) : h.homEquiv.symm ((Functor.const _).map g ≫ f) = g ≫ h.homEquiv.symm f := h.homEquiv.injective (by aesop) /-- The universal property of a limit cone: a map `W ⟶ X` is the same as - a cone on `F` with cone point `W`. -/ -def homIso (h : IsLimit t) (W : C) : - (ULift.{u₁} (W ⟶ t.pt : Type v₃)) ≅ ((const J).obj W ⟶ F) := +a cone on `F` with cone point `W`. -/ +@[to_dual +/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as +a cocone on `F` with cone point `W`. -/] +def homIso (h : IsLimit t) (W : C) : ULift.{u₁} (W ⟶ t.pt : Type v₃) ≅ (const J).obj W ⟶ F := Equiv.toIso (Equiv.ulift.trans h.homEquiv) +-- TODO: `to_dual` doesn't yet know that it shouldn't translate the category on `Type _`. @[simp] theorem homIso_hom (h : IsLimit t) {W : C} : (IsLimit.homIso h W).hom = ↾fun f ↦ (t.extend f.down).π := @@ -417,9 +535,12 @@ def homIso' (h : IsLimit t) (W : C) : { app := fun j => p.1 j naturality := fun j j' f => by dsimp; rw [id_comp]; exact (p.2 f).symm } } -/-- If G : C → D is a faithful functor which sends t to a limit cone, - then it suffices to check that the induced maps for the image of t - can be lifted to maps of C. -/ +/-- If `G : C → D` is a faithful functor which sends t to a limit cone, +then it suffices to check that the induced maps for the image of t +can be lifted to maps of `C`. -/ +@[to_dual /-- If `G : C → D` is a faithful functor which sends t to a colimit cocone, +then it suffices to check that the induced maps for the image of t +can be lifted to maps of `C`. -/] def ofFaithful {t : Cone F} {D : Type u₄} [Category.{v₄} D] (G : C ⥤ D) [G.Faithful] (ht : IsLimit (mapCone G t)) (lift : ∀ s : Cone F, s.pt ⟶ t.pt) (h : ∀ s, G.map (lift s) = ht.lift (mapCone G s)) : IsLimit t := @@ -434,11 +555,16 @@ def ofFaithful {t : Cone F} {D : Type u₄} [Category.{v₄} D] (G : C ⥤ D) [G /-- If `F` and `G` are naturally isomorphic, then `F.mapCone c` being a limit implies `G.mapCone c` is also a limit. -/ +@[to_dual +/-- If `F` and `G` are naturally isomorphic, then `F.mapCocone c` being a colimit implies +`G.mapCocone c` is also a colimit. +-/] def mapConeEquiv {D : Type u₄} [Category.{v₄} D] {K : J ⥤ C} {F G : C ⥤ D} (h : F ≅ G) {c : Cone K} (t : IsLimit (mapCone F c)) : IsLimit (mapCone G c) := by apply postcomposeInvEquiv (isoWhiskerLeft K h :) (mapCone G c) _ apply t.ofIsoLimit (postcomposeWhiskerLeftMapCone h.symm c).symm +-- TODO: `to_dual` doesn't yet know that it shouldn't translate the category on `Type _`. /-- A cone is a limit cone exactly if there is a unique cone morphism from any other cone. -/ @@ -533,369 +659,12 @@ end end IsLimit -/-- A cocone `t` on `F` is a colimit cocone if each cocone on `F` admits a unique -cocone morphism from `t`. -/ -@[stacks 002F] -structure IsColimit (t : Cocone F) where - /-- `t.pt` maps to all other cocone covertices -/ - desc : ∀ s : Cocone F, t.pt ⟶ s.pt - /-- The map `desc` makes the diagram with the natural transformations commute -/ - fac : ∀ (s : Cocone F) (j : J), t.ι.app j ≫ desc s = s.ι.app j := by cat_disch - /-- `desc` is the unique such map -/ - uniq : - ∀ (s : Cocone F) (m : t.pt ⟶ s.pt) (_ : ∀ j : J, t.ι.app j ≫ m = s.ι.app j), m = desc s := by - cat_disch - -attribute [reassoc (attr := simp)] IsColimit.fac namespace IsColimit -instance subsingleton {t : Cocone F} : Subsingleton (IsColimit t) := - ⟨by intro P Q; cases P; cases Q; congr; cat_disch⟩ - -/-- Given a natural transformation `α : F ⟶ G`, we give a morphism from the cocone point -of a colimit cocone over `F` to the cocone point of any cocone over `G`. -/ -def map {F G : J ⥤ C} {s : Cocone F} (P : IsColimit s) (t : Cocone G) (α : F ⟶ G) : s.pt ⟶ t.pt := - P.desc ((Cocone.precompose α).obj t) - -set_option backward.isDefEq.respectTransparency false in -- This is needed in CategoryTheory/Limits/Shapes/Biproducts.lean -@[reassoc (attr := simp)] -theorem ι_map {F G : J ⥤ C} {c : Cocone F} (hc : IsColimit c) (d : Cocone G) (α : F ⟶ G) (j : J) : - c.ι.app j ≫ IsColimit.map hc d α = α.app j ≫ d.ι.app j := - fac _ _ _ - -@[simp] -theorem desc_self {t : Cocone F} (h : IsColimit t) : h.desc t = 𝟙 t.pt := - (h.uniq _ _ fun _ => comp_id _).symm - -set_option backward.isDefEq.respectTransparency false in --- Repackaging the definition in terms of cocone morphisms. -/-- The universal morphism from a colimit cocone to any other cocone. -/ -@[simps] -def descCoconeMorphism {t : Cocone F} (h : IsColimit t) (s : Cocone F) : t ⟶ s where hom := h.desc s - -theorem uniq_cocone_morphism {s t : Cocone F} (h : IsColimit t) {f f' : t ⟶ s} : f = f' := - have : ∀ {g : t ⟶ s}, g = h.descCoconeMorphism s := by - intro g; ext; exact h.uniq _ _ g.w - this.trans this.symm - -/-- Restating the definition of a colimit cocone in terms of the ∃! operator. -/ -theorem existsUnique {t : Cocone F} (h : IsColimit t) (s : Cocone F) : - ∃! d : t.pt ⟶ s.pt, ∀ j, t.ι.app j ≫ d = s.ι.app j := - ⟨h.desc s, h.fac s, h.uniq s⟩ - -/-- Noncomputably make a colimit cocone from the existence of unique factorizations. -/ -def ofExistsUnique {t : Cocone F} - (ht : ∀ s : Cocone F, ∃! d : t.pt ⟶ s.pt, ∀ j, t.ι.app j ≫ d = s.ι.app j) : IsColimit t := by - choose s hs hs' using ht - exact ⟨s, hs, hs'⟩ - -set_option backward.defeqAttrib.useBackward true in -/-- Alternative constructor for `IsColimit`, -providing a morphism of cocones rather than a morphism between the cocone points -and separately the factorisation condition. --/ -@[simps] -def mkCoconeMorphism {t : Cocone F} (desc : ∀ s : Cocone F, t ⟶ s) - (uniq' : ∀ (s : Cocone F) (m : t ⟶ s), m = desc s) : IsColimit t where - desc s := (desc s).hom - uniq s m w := - have : CoconeMorphism.mk m w = desc s := by apply uniq' - congrArg CoconeMorphism.hom this - -/-- Colimit cocones on `F` are unique up to isomorphism. -/ -@[simps] -def uniqueUpToIso {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) : s ≅ t where - hom := P.descCoconeMorphism t - inv := Q.descCoconeMorphism s - hom_inv_id := P.uniq_cocone_morphism - inv_hom_id := Q.uniq_cocone_morphism - -/-- Any cocone morphism between colimit cocones is an isomorphism. -/ -theorem hom_isIso {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) (f : s ⟶ t) : IsIso f := - ⟨⟨Q.descCoconeMorphism s, ⟨P.uniq_cocone_morphism, Q.uniq_cocone_morphism⟩⟩⟩ - -/-- Colimits of `F` are unique up to isomorphism. -/ -def coconePointUniqueUpToIso {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) : s.pt ≅ t.pt := - (Cocone.forget F).mapIso (uniqueUpToIso P Q) - -@[reassoc (attr := simp)] -theorem comp_coconePointUniqueUpToIso_hom {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) - (j : J) : s.ι.app j ≫ (coconePointUniqueUpToIso P Q).hom = t.ι.app j := - (uniqueUpToIso P Q).hom.w _ - -@[reassoc (attr := simp)] -theorem comp_coconePointUniqueUpToIso_inv {s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) - (j : J) : t.ι.app j ≫ (coconePointUniqueUpToIso P Q).inv = s.ι.app j := - (uniqueUpToIso P Q).inv.w _ - -set_option backward.isDefEq.respectTransparency false in -@[reassoc (attr := simp)] -theorem coconePointUniqueUpToIso_hom_desc {r s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) : - (coconePointUniqueUpToIso P Q).hom ≫ Q.desc r = P.desc r := - P.uniq _ _ (by simp) - -set_option backward.isDefEq.respectTransparency false in -@[reassoc (attr := simp)] -theorem coconePointUniqueUpToIso_inv_desc {r s t : Cocone F} (P : IsColimit s) (Q : IsColimit t) : - (coconePointUniqueUpToIso P Q).inv ≫ P.desc r = Q.desc r := - Q.uniq _ _ (by simp) - -/-- Transport evidence that a cocone is a colimit cocone across an isomorphism of cocones. -/ -def ofIsoColimit {r t : Cocone F} (P : IsColimit r) (i : r ≅ t) : IsColimit t := - IsColimit.mkCoconeMorphism (fun s => i.inv ≫ P.descCoconeMorphism s) fun s m => by - rw [i.eq_inv_comp]; apply P.uniq_cocone_morphism - -@[simp] -theorem ofIsoColimit_desc {r t : Cocone F} (P : IsColimit r) (i : r ≅ t) (s) : - (P.ofIsoColimit i).desc s = i.inv.hom ≫ P.desc s := - rfl - -/-- Isomorphism of cocones preserves whether or not they are colimiting cocones. -/ -def equivIsoColimit {r t : Cocone F} (i : r ≅ t) : IsColimit r ≃ IsColimit t where - toFun h := h.ofIsoColimit i - invFun h := h.ofIsoColimit i.symm - left_inv := by cat_disch - right_inv := by cat_disch - -@[simp] -theorem equivIsoColimit_apply {r t : Cocone F} (i : r ≅ t) (P : IsColimit r) : - equivIsoColimit i P = P.ofIsoColimit i := - rfl - -@[simp] -theorem equivIsoColimit_symm_apply {r t : Cocone F} (i : r ≅ t) (P : IsColimit t) : - (equivIsoColimit i).symm P = P.ofIsoColimit i.symm := - rfl - -/-- If the canonical morphism to a cocone point from a colimiting cocone point is an iso, then the -first cocone was colimiting also. --/ -def ofPointIso {r t : Cocone F} (P : IsColimit r) [i : IsIso (P.desc t)] : IsColimit t := - ofIsoColimit P (by - haveI : IsIso (P.descCoconeMorphism t).hom := i - haveI : IsIso (P.descCoconeMorphism t) := Cocone.cocone_iso_of_hom_iso _ - apply asIso (P.descCoconeMorphism t)) variable {t : Cocone F} -set_option backward.defeqAttrib.useBackward true in -theorem hom_desc (h : IsColimit t) {W : C} (m : t.pt ⟶ W) : - m = - h.desc - { pt := W - ι := { app := fun b => t.ι.app b ≫ m } } := - h.uniq - { pt := W - ι := { app := fun b => t.ι.app b ≫ m } } - m fun _ => rfl - -/-- Two morphisms out of a colimit are equal if their compositions with - each cocone morphism are equal. -/ -theorem hom_ext (h : IsColimit t) {W : C} {f f' : t.pt ⟶ W} - (w : ∀ j, t.ι.app j ≫ f = t.ι.app j ≫ f') : f = f' := by - rw [h.hom_desc f, h.hom_desc f']; congr; exact funext w - -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in -lemma nonempty_isColimit_iff_isIso_desc {s t : Cocone F} (hs : IsColimit s) : - Nonempty (IsColimit t) ↔ IsIso (hs.desc t) := - ⟨fun ⟨ht⟩ ↦ ⟨ht.desc s, hs.hom_ext (by simp), ht.hom_ext (by simp)⟩, fun h ↦ ⟨hs.ofPointIso⟩⟩ - -/-- Given a left adjoint functor between categories of cocones, -the image of a colimit cocone is a colimit cocone. --/ -def ofLeftAdjoint {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} {left : Cocone G ⥤ Cocone F} - {right : Cocone F ⥤ Cocone G} (adj : left ⊣ right) {c : Cocone G} (t : IsColimit c) : - IsColimit (left.obj c) := - mkCoconeMorphism - (fun s => (adj.homEquiv c s).symm (t.descCoconeMorphism _)) fun _ _ => - (Adjunction.homEquiv_apply_eq _ _ _).1 t.uniq_cocone_morphism - -/-- Given two functors which have equivalent categories of cocones, -we can transport a colimiting cocone across the equivalence. --/ -def ofCoconeEquiv {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} (h : Cocone G ≌ Cocone F) - {c : Cocone G} : IsColimit (h.functor.obj c) ≃ IsColimit c where - toFun P := ofIsoColimit (ofLeftAdjoint h.symm.toAdjunction P) (h.unitIso.symm.app c) - invFun := ofLeftAdjoint h.toAdjunction - left_inv := by cat_disch - right_inv := by cat_disch - -@[simp] -theorem ofCoconeEquiv_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} - (h : Cocone G ≌ Cocone F) {c : Cocone G} (P : IsColimit (h.functor.obj c)) (s) : - (ofCoconeEquiv h P).desc s = - (h.unit.app c).hom ≫ - (h.inverse.map (P.descCoconeMorphism (h.functor.obj s))).hom ≫ (h.unitInv.app s).hom := - rfl - -@[simp] -theorem ofCoconeEquiv_symm_apply_desc {D : Type u₄} [Category.{v₄} D] {G : K ⥤ D} - (h : Cocone G ≌ Cocone F) {c : Cocone G} (P : IsColimit c) (s) : - ((ofCoconeEquiv h).symm P).desc s = - (h.functor.map (P.descCoconeMorphism (h.inverse.obj s))).hom ≫ (h.counit.app s).hom := - rfl - -/-- A cocone precomposed with the inverse of a natural isomorphism is a colimit cocone -if and only if the original cocone is. --/ -def precomposeInvEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cocone F) : - IsColimit ((Cocone.precompose α.inv).obj c) ≃ IsColimit c := - ofCoconeEquiv (Cocone.precomposeEquivalence α) - -/-- A cocone precomposed with a natural isomorphism is a colimit cocone -if and only if the original cocone is. --/ -def precomposeHomEquiv {F G : J ⥤ C} (α : F ≅ G) (c : Cocone G) : - IsColimit ((Cocone.precompose α.hom).obj c) ≃ IsColimit c := - precomposeInvEquiv α.symm c - -/-- Constructing an equivalence `is_colimit c ≃ is_colimit d` from a natural isomorphism -between the underlying functors, and then an isomorphism between `c` transported along this and `d`. --/ -def equivOfNatIsoOfIso {F G : J ⥤ C} (α : F ≅ G) (c : Cocone F) (d : Cocone G) - (w : (Cocone.precompose α.inv).obj c ≅ d) : IsColimit c ≃ IsColimit d := - (precomposeInvEquiv α _).symm.trans (equivIsoColimit w) - -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in -/-- The cocone points of two colimit cocones for naturally isomorphic functors -are themselves isomorphic. --/ -@[simps] -def coconePointsIsoOfNatIso {F G : J ⥤ C} {s : Cocone F} {t : Cocone G} (P : IsColimit s) - (Q : IsColimit t) (w : F ≅ G) : s.pt ≅ t.pt where - hom := P.map t w.hom - inv := Q.map s w.inv - hom_inv_id := P.hom_ext (by simp) - inv_hom_id := Q.hom_ext (by simp) - -@[reassoc] -theorem comp_coconePointsIsoOfNatIso_hom {F G : J ⥤ C} {s : Cocone F} {t : Cocone G} - (P : IsColimit s) (Q : IsColimit t) (w : F ≅ G) (j : J) : - s.ι.app j ≫ (coconePointsIsoOfNatIso P Q w).hom = w.hom.app j ≫ t.ι.app j := by simp - -@[reassoc] -theorem comp_coconePointsIsoOfNatIso_inv {F G : J ⥤ C} {s : Cocone F} {t : Cocone G} - (P : IsColimit s) (Q : IsColimit t) (w : F ≅ G) (j : J) : - t.ι.app j ≫ (coconePointsIsoOfNatIso P Q w).inv = w.inv.app j ≫ s.ι.app j := by simp - -set_option backward.isDefEq.respectTransparency false in -@[reassoc] -theorem coconePointsIsoOfNatIso_hom_desc {F G : J ⥤ C} {s : Cocone F} {r t : Cocone G} - (P : IsColimit s) (Q : IsColimit t) (w : F ≅ G) : - (coconePointsIsoOfNatIso P Q w).hom ≫ Q.desc r = P.map _ w.hom := - P.hom_ext (by simp) - -set_option backward.isDefEq.respectTransparency false in -@[reassoc] -theorem coconePointsIsoOfNatIso_inv_desc {F G : J ⥤ C} {s : Cocone G} {r t : Cocone F} - (P : IsColimit t) (Q : IsColimit s) (w : F ≅ G) : - (coconePointsIsoOfNatIso P Q w).inv ≫ P.desc r = Q.map _ w.inv := - Q.hom_ext (by simp) - -section Equivalence - -open CategoryTheory.Equivalence - -/-- If `s : Cocone F` is a colimit cocone, so is `s` whiskered by an equivalence `e`. --/ -def whiskerEquivalence {s : Cocone F} (P : IsColimit s) (e : K ≌ J) : - IsColimit (s.whisker e.functor) := - ofLeftAdjoint (Cocone.whiskeringEquivalence e).toAdjunction P - -/-- If `s : Cocone F` whiskered by an equivalence `e` is a colimit cocone, so is `s`. --/ -def ofWhiskerEquivalence {s : Cocone F} (e : K ≌ J) (P : IsColimit (s.whisker e.functor)) : - IsColimit s := - equivIsoColimit ((Cocone.whiskeringEquivalence e).unitIso.app s).symm - (ofLeftAdjoint (Cocone.whiskeringEquivalence e).symm.toAdjunction P) - -/-- Given an equivalence of diagrams `e`, `s` is a colimit cocone iff `s.whisker e.functor` is. --/ -def whiskerEquivalenceEquiv {s : Cocone F} (e : K ≌ J) : - IsColimit s ≃ IsColimit (s.whisker e.functor) := - ⟨fun h => h.whiskerEquivalence e, ofWhiskerEquivalence e, by cat_disch, by cat_disch⟩ - -/-- A colimit cocone extended by an isomorphism is a colimit cocone. -/ -def extendIso {s : Cocone F} {X : C} (i : s.pt ⟶ X) [IsIso i] (hs : IsColimit s) : - IsColimit (s.extend i) := - IsColimit.ofIsoColimit hs (Cocone.extendIso s (asIso i)) - -/-- A cocone is a colimit cocone if its extension by an isomorphism is. -/ -def ofExtendIso {s : Cocone F} {X : C} (i : s.pt ⟶ X) [IsIso i] (hs : IsColimit (s.extend i)) : - IsColimit s := - IsColimit.ofIsoColimit hs (Cocone.extendIso s (asIso i)).symm - -/-- A cocone is a colimit cocone iff its extension by an isomorphism is. -/ -def extendIsoEquiv {s : Cocone F} {X : C} (i : s.pt ⟶ X) [IsIso i] : - IsColimit s ≃ IsColimit (s.extend i) := - equivOfSubsingletonOfSubsingleton (extendIso i) (ofExtendIso i) - -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in -/-- We can prove two cocone points `(s : Cocone F).pt` and `(t : Cocone G).pt` are isomorphic if -* both cocones are colimit cocones -* their indexing categories are equivalent via some `e : J ≌ K`, -* the triangle of functors commutes up to a natural isomorphism: `e.functor ⋙ G ≅ F`. - -This is the most general form of uniqueness of cocone points, -allowing relabelling of both the indexing category (up to equivalence) -and the functor (up to natural isomorphism). --/ -@[simps] -def coconePointsIsoOfEquivalence {F : J ⥤ C} {s : Cocone F} {G : K ⥤ C} {t : Cocone G} - (P : IsColimit s) (Q : IsColimit t) (e : J ≌ K) (w : e.functor ⋙ G ≅ F) : s.pt ≅ t.pt := - let w' : e.inverse ⋙ F ≅ G := (isoWhiskerLeft e.inverse w).symm ≪≫ invFunIdAssoc e G - { hom := P.desc ((Cocone.equivalenceOfReindexing e w).functor.obj t) - inv := Q.desc ((Cocone.equivalenceOfReindexing e.symm w').functor.obj s) - hom_inv_id := by - apply hom_ext P; intro j - dsimp [w'] - simp only [Limits.Cocone.whisker_ι, fac, invFunIdAssoc_inv_app, whiskerLeft_app, assoc, - comp_id, Limits.Cocone.precompose_obj_ι, fac_assoc, NatTrans.comp_app] - rw [counitInv_app_functor, ← Functor.comp_map, ← w.inv.naturality_assoc] - simp - inv_hom_id := by - apply hom_ext Q - cat_disch } - -end Equivalence - -set_option backward.isDefEq.respectTransparency false in -/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as - a cocone on `F` with cone point `W`. -/ -def homEquiv (h : IsColimit t) {W : C} : (t.pt ⟶ W) ≃ (F ⟶ (const J).obj W) where - toFun f := (t.extend f).ι - invFun ι := h.desc - { pt := W - ι } - left_inv f := h.hom_ext (by simp) - right_inv ι := by cat_disch - -@[simp] -lemma homEquiv_apply (h : IsColimit t) {W : C} (f : t.pt ⟶ W) : - h.homEquiv f = (t.extend f).ι := rfl - -@[reassoc (attr := simp)] -lemma ι_app_homEquiv_symm (h : IsColimit t) {W : C} - (f : F ⟶ (const J).obj W) (j : J) : - t.ι.app j ≫ h.homEquiv.symm f = f.app j := by - simp [homEquiv] - -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in -lemma homEquiv_symm_naturality (h : IsColimit t) {W W' : C} - (f : F ⟶ (const J).obj W) (g : W ⟶ W') : - h.homEquiv.symm (f ≫ (Functor.const _).map g) = h.homEquiv.symm f ≫ g := - h.homEquiv.injective (by aesop) - -/-- The universal property of a colimit cocone: a map `X ⟶ W` is the same as - a cocone on `F` with cone point `W`. -/ -def homIso (h : IsColimit t) (W : C) : - ULift.{u₁} (t.pt ⟶ W : Type v₃) ≅ (F ⟶ (const J).obj W) := - Equiv.toIso (Equiv.ulift.trans h.homEquiv) @[simp] theorem homIso_hom (h : IsColimit t) {W : C} : @@ -921,28 +690,6 @@ def homIso' (h : IsColimit t) (W : C) : { app := fun j => p.1 j naturality := fun j j' f => by dsimp; rw [comp_id]; exact p.2 f } } -set_option backward.isDefEq.respectTransparency false in -/-- If G : C → D is a faithful functor which sends t to a colimit cocone, - then it suffices to check that the induced maps for the image of t - can be lifted to maps of C. -/ -def ofFaithful {t : Cocone F} {D : Type u₄} [Category.{v₄} D] (G : C ⥤ D) [G.Faithful] - (ht : IsColimit (mapCocone G t)) (desc : ∀ s : Cocone F, t.pt ⟶ s.pt) - (h : ∀ s, G.map (desc s) = ht.desc (mapCocone G s)) : IsColimit t := - { desc - fac := fun s j => by apply G.map_injective; rw [G.map_comp, h]; apply ht.fac - uniq := fun s m w => by - apply G.map_injective; rw [h] - refine ht.uniq (mapCocone G s) _ fun j => ?_ - convert! ← congrArg (fun f => G.map f) (w j) - apply G.map_comp } - -/-- If `F` and `G` are naturally isomorphic, then `F.mapCocone c` being a colimit implies -`G.mapCocone c` is also a colimit. --/ -def mapCoconeEquiv {D : Type u₄} [Category.{v₄} D] {K : J ⥤ C} {F G : C ⥤ D} (h : F ≅ G) - {c : Cocone K} (t : IsColimit (mapCocone F c)) : IsColimit (mapCocone G c) := by - apply IsColimit.ofIsoColimit _ (precomposeWhiskerLeftMapCocone h c) - apply (precomposeInvEquiv (isoWhiskerLeft K h :) _).symm t set_option backward.defeqAttrib.useBackward true in /-- A cocone is a colimit cocone exactly if diff --git a/Mathlib/CategoryTheory/Limits/MonoCoprod.lean b/Mathlib/CategoryTheory/Limits/MonoCoprod.lean index 3cf2f524ae1b5e..8f6fd6777fbe73 100644 --- a/Mathlib/CategoryTheory/Limits/MonoCoprod.lean +++ b/Mathlib/CategoryTheory/Limits/MonoCoprod.lean @@ -76,6 +76,7 @@ instance {A B : C} [MonoCoprod C] [HasBinaryCoproduct A B] : Mono (coprod.inl : instance {A B : C} [MonoCoprod C] [HasBinaryCoproduct A B] : Mono (coprod.inr : B ⟶ A ⨿ B) := binaryCofan_inr _ (colimit.isColimit _) +set_option backward.isDefEq.respectTransparency false in theorem mono_inl_iff {A B : C} {c₁ c₂ : BinaryCofan A B} (hc₁ : IsColimit c₁) (hc₂ : IsColimit c₂) : Mono c₁.inl ↔ Mono c₂.inl := by suffices diff --git a/Mathlib/CategoryTheory/Limits/MorphismProperty.lean b/Mathlib/CategoryTheory/Limits/MorphismProperty.lean index 469c6f3a5e85bd..66f8eb57f06f89 100644 --- a/Mathlib/CategoryTheory/Limits/MorphismProperty.lean +++ b/Mathlib/CategoryTheory/Limits/MorphismProperty.lean @@ -121,7 +121,7 @@ lemma CostructuredArrow.isClosedUnderColimitsOfShape {J : Type*} [Category* J] isColimitOfPreserves _ d.isColimit have heq : Y.hom = hd.desc { pt := X, ι := { app j := (d.diag.obj j).hom } } := by refine hd.hom_ext fun j ↦ ?_ - simp only [Functor.const_obj_obj, IsColimit.fac] + simp only [IsColimit.fac] simp rw [P.costructuredArrowObj_iff, heq, ← hd.coconePointUniqueUpToIso_hom_desc (hc _), P.cancel_left_of_respectsIso] diff --git a/Mathlib/CategoryTheory/Limits/Preorder.lean b/Mathlib/CategoryTheory/Limits/Preorder.lean index 2bbcb334ca57be..a4da980b0331aa 100644 --- a/Mathlib/CategoryTheory/Limits/Preorder.lean +++ b/Mathlib/CategoryTheory/Limits/Preorder.lean @@ -6,6 +6,7 @@ Authors: Sina Hazratpour, Joël Riou, Fernando Chu module public import Mathlib.CategoryTheory.Limits.Shapes.BinaryProducts +public import Mathlib.CategoryTheory.Limits.Shapes.Products public import Mathlib.Order.Bounds.Defs /-! @@ -211,4 +212,18 @@ instance (priority := low) [SemilatticeSup C] : HasBinaryCoproducts C where end +section + +/-- The product of elements in a complete lattice is the infimum. -/ +def isLimitIInf [CompleteLattice C] {ι : Type*} (X : ι → C) : + IsLimit (Fan.mk (⨅ i, X i) fun i : ι ↦ homOfLE (iInf_le X i)) := + isLimitOfIsGLB _ _ (by simp [isGLB_iInf]) + +/-- The coproduct of elements in a complete lattice is the supremum. -/ +def isColimitISup [CompleteLattice C] {ι : Type*} (X : ι → C) : + IsColimit (Cofan.mk (⨆ i, X i) fun i : ι ↦ homOfLE (le_iSup X i)) := + isColimitOfIsLUB _ _ (by simp [isLUB_iSup]) + +end + end Preorder diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean b/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean index 1967dc99751bd3..36706e0f7f9b90 100644 --- a/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean +++ b/Mathlib/CategoryTheory/Limits/Preserves/Basic.lean @@ -80,7 +80,7 @@ diagram `J ⥤ C` to limit cones, where `J : Type u` with `[Category.{v} J]`. -/ -- `PreservesLimitsOfSize`, `PreservesColimitsOfSize`, `ReflectsLimitsOfSize`, and -- `ReflectsColimitsOfSize` would default to universe output parameters. -- See Note [universe output parameters and typeclass caching]. -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class PreservesLimitsOfSize (F : C ⥤ D) : Prop where preservesLimitsOfShape : ∀ {J : Type w} [Category.{w'} J], PreservesLimitsOfShape J F := by infer_instance @@ -93,7 +93,7 @@ abbrev PreservesLimits (F : C ⥤ D) := -- This should be used with explicit universe variables. /-- `PreservesColimitsOfSize.{v u} F` means that `F` sends all colimit cocones over any diagram `J ⥤ C` to colimit cocones, where `J : Type u` with `[Category.{v} J]`. -/ -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class PreservesColimitsOfSize (F : C ⥤ D) : Prop where preservesColimitsOfShape : ∀ {J : Type w} [Category.{w'} J], PreservesColimitsOfShape J F := by infer_instance @@ -415,7 +415,7 @@ whenever the image of a cone over some `K : J ⥤ C` under `F` is a limit cone i the cone was already a limit cone in `C`. Note that we do not assume a priori that `D` actually has any limits. -/ -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class ReflectsLimitsOfSize (F : C ⥤ D) : Prop where reflectsLimitsOfShape : ∀ {J : Type w} [Category.{w'} J], ReflectsLimitsOfShape J F := by infer_instance @@ -434,7 +434,7 @@ whenever the image of a cocone over some `K : J ⥤ C` under `F` is a colimit co the cocone was already a colimit cocone in `C`. Note that we do not assume a priori that `D` actually has any colimits. -/ -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class ReflectsColimitsOfSize (F : C ⥤ D) : Prop where reflectsColimitsOfShape : ∀ {J : Type w} [Category.{w'} J], ReflectsColimitsOfShape J F := by infer_instance @@ -778,7 +778,7 @@ lemma isIso_app_coconePt_of_preservesColimit (isColimitOfPreserves L hc) (isColimitOfPreserves L' hc) (asIso (whiskerLeft K α)) convert! (inferInstance : IsIso e.hom) apply (isColimitOfPreserves L hc).hom_ext fun j ↦ ?_ - simp only [Functor.comp_obj, Functor.mapCocone_pt, Functor.const_obj_obj, Functor.mapCocone_ι_app, + simp only [Functor.comp_obj, Functor.mapCocone_pt, Functor.mapCocone_ι_app, NatTrans.naturality, IsColimit.coconePointsIsoOfNatIso_hom, asIso_hom, e] refine (((isColimitOfPreserves L hc).ι_map (L'.mapCocone c) (whiskerLeft K α) j).trans ?_).symm simp diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean b/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean index 0c96c6d0d6de04..5dbce868ff5ce4 100644 --- a/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean +++ b/Mathlib/CategoryTheory/Limits/Preserves/Bifunctor.lean @@ -146,6 +146,7 @@ variable {c₁ : Cocone K₁} (hc₁ : IsColimit c₁) {c₃ : Cocone <| uncurry.obj (whiskeringLeft₂ C |>.obj K₁ |>.obj K₂ |>.obj G)} (hc₃ : IsColimit c₃) +set_option backward.isDefEq.respectTransparency false in /-- Characterize the inverse direction of the isomorphism `PreservesColimit₂.isoObjCoconePointsOfIsColimit` w.r.t. the canonical maps to the colimit. -/ @[reassoc (attr := simp)] @@ -231,10 +232,9 @@ instance of_preservesColimits_in_each_variable apply (P j₁).hom_ext intro j₂ haveI := (P j₁).fac s j₂ + simp only [Functor.mapCocone_pt, Functor.mapCocone_ι_app, Q₀, s] at this simp only [Functor.mapCocone_pt, - Functor.const_obj_obj, Functor.mapCocone_ι_app, Q₀, s] at this - simp only [Functor.mapCocone_pt, - Functor.const_obj_obj, Functor.mapCocone_ι_app, NatTrans.naturality, this, Q₀, s]) + Functor.mapCocone_ι_app, NatTrans.naturality, this, Q₀, s]) ⟨IsColimit.ofCoconeUncurry P <| IsColimit.precomposeHomEquiv E₀ _ <| IsColimit.ofIsoColimit (isColimitOfPreserves _ hc₁) E₁.symm⟩ diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean b/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean index 693fdffbdb2041..6e746297d1eeca 100644 --- a/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean +++ b/Mathlib/CategoryTheory/Limits/Preserves/Filtered.lean @@ -10,6 +10,7 @@ public import Mathlib.CategoryTheory.Filtered.Basic /-! # Preservation of filtered colimits and cofiltered limits. + Typically forgetful functors from algebraic categories preserve filtered colimits (although not general colimits). See e.g. `Mathlib/Algebra/Category/MonCat/FilteredColimits.lean`. @@ -48,7 +49,7 @@ filtered diagram `J ⥤ C` to colimit cocones, where `J : Type w` with `[Categor -- `PreservesFilteredColimitsOfSize`, `ReflectsFilteredColimitsOfSize`, -- `PreservesCofilteredLimitsOfSize`, and `ReflectsCofilteredLimitsOfSize` would default to -- universe output parameters. See Note [universe output parameters and typeclass caching]. -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class PreservesFilteredColimitsOfSize (F : C ⥤ D) : Prop where preserves_filtered_colimits : ∀ (J : Type w) [Category.{w'} J] [IsFiltered J], PreservesColimitsOfShape J F @@ -104,7 +105,7 @@ section Reflects -- This should be used with explicit universe variables. /-- `ReflectsFilteredColimitsOfSize.{w', w} F` means that whenever the image of a filtered cocone under `F` is a colimit cocone, the original cocone was already a colimit. -/ -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class ReflectsFilteredColimitsOfSize (F : C ⥤ D) : Prop where reflects_filtered_colimits : ∀ (J : Type w) [Category.{w'} J] [IsFiltered J], ReflectsColimitsOfShape J F @@ -164,7 +165,7 @@ section Preserves -- This should be used with explicit universe variables. /-- `PreservesCofilteredLimitsOfSize.{w', w} F` means that `F` sends all limit cones over any cofiltered diagram `J ⥤ C` to limit cones, where `J : Type w` with `[Category.{w'} J]`. -/ -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class PreservesCofilteredLimitsOfSize (F : C ⥤ D) : Prop where preserves_cofiltered_limits : ∀ (J : Type w) [Category.{w'} J] [IsCofiltered J], PreservesLimitsOfShape J F @@ -220,7 +221,7 @@ section Reflects -- This should be used with explicit universe variables. /-- `ReflectsCofilteredLimitsOfSize.{w', w} F` means that whenever the image of a cofiltered cone under `F` is a limit cone, the original cone was already a limit. -/ -@[univ_out_params, nolint checkUnivs, pp_with_univ] +@[univ_out_params, pp_with_univ] class ReflectsCofilteredLimitsOfSize (F : C ⥤ D) : Prop where reflects_cofiltered_limits : ∀ (J : Type w) [Category.{w'} J] [IsCofiltered J], ReflectsLimitsOfShape J F diff --git a/Mathlib/CategoryTheory/Limits/Preserves/Lattice.lean b/Mathlib/CategoryTheory/Limits/Preserves/Lattice.lean new file mode 100644 index 00000000000000..627b4e7d2f6cab --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/Preserves/Lattice.lean @@ -0,0 +1,104 @@ +/- +Copyright (c) 2026 Brian Nugent. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Brian Nugent +-/ +module + +public import Mathlib.CategoryTheory.Limits.Lattice +public import Mathlib.CategoryTheory.Limits.Preserves.Finite +public import Mathlib.Order.ConditionallyCompleteLattice.Basic +public import Mathlib.Order.Hom.CompleteLattice + +/-! +# Lattice Homs that Preserve Limits and Colimits + +This file provides instances for when OrderHom.toFunctor preserves limits/colimits. +In particular, if `f` preserves finite infs/sups (i.e. is from a InfTopHomClass/SupBotHomClass) +then `(toOrderHom f).toFunctor` preserves finite limits/colimits. If `f` preserves +arbitrary infs/sups (i.e. is from a sInfHomClass/sSupHomClass) then `(toOrderHom f).toFunctor` +preserves all limits/colimits. + +-/ + +public section + +open OrderHomClass + +namespace CategoryTheory.Limits.CompleteLattice + +universe w w' u v + +variable {α : Type u} {β : Type v} {F : Type*} [FunLike F α β] (f : F) + +section + +variable [SemilatticeInf α] [OrderTop α] [SemilatticeInf β] [OrderTop β] [InfTopHomClass F α β] + +instance preservesLimit_finite_toFunctor {J : Type w} [SmallCategory J] + [FinCategory J] (K : J ⥤ α) : PreservesLimit K (toOrderHom f).toFunctor := + preservesLimit_of_preserves_limit_cone (finiteLimitCone K).isLimit <| + (finiteLimitCone _).isLimit.ofIsoLimit + (Cone.ext (eqToIso (show Finset.univ.inf _ = f _ by aesop)) (by subsingleton)) + +instance preservesLimitsOfShape_finite_toFunctor {J : Type w} [SmallCategory J] [FinCategory J] : + PreservesLimitsOfShape J (toOrderHom f).toFunctor where + +instance : PreservesFiniteLimits (toOrderHom f).toFunctor where + preservesFiniteLimits _ _ _ := inferInstance + +end + +section + +variable [SemilatticeSup α] [OrderBot α] [SemilatticeSup β] [OrderBot β] [SupBotHomClass F α β] + +instance preservesColimit_finite_toFunctor {J : Type w} [SmallCategory J] + [FinCategory J] (K : J ⥤ α) : PreservesColimit K (toOrderHom f).toFunctor := + preservesColimit_of_preserves_colimit_cocone (finiteColimitCocone K).isColimit <| + (finiteColimitCocone _).isColimit.ofIsoColimit + (Cocone.ext (eqToIso (show Finset.univ.sup _ = f _ by aesop)) (by subsingleton)) + +instance preservesColimitsOfShape_finite_toFunctor {J : Type w} [SmallCategory J] + [FinCategory J] : PreservesColimitsOfShape J (toOrderHom f).toFunctor where + +instance : PreservesFiniteColimits (toOrderHom f).toFunctor where + preservesFiniteColimits _ _ _ := inferInstance + +end + +section + +variable [CompleteLattice α] [CompleteLattice β] + +instance preservesLimit_toFunctor [sInfHomClass F α β] {J : Type w} [Category.{w'} J] + (K : J ⥤ α) : PreservesLimit K (toOrderHom f).toFunctor := + preservesLimit_of_preserves_limit_cone (limitCone K).isLimit <| + (limitCone _).isLimit.ofIsoLimit (Cone.ext (eqToIso (by aesop)) (by subsingleton)) + +instance preservesLimitsOfShape_toFunctor [sInfHomClass F α β] {J : Type w} [Category.{w'} J] : + PreservesLimitsOfShape J (toOrderHom f).toFunctor where + +instance preservesLimitsOfSize_toFunctor [sInfHomClass F α β] : + PreservesLimitsOfSize.{w', w} (toOrderHom f).toFunctor where + +instance preservesLimits_toFunctor [sInfHomClass F α β] : + PreservesLimits (toOrderHom f).toFunctor where + +instance preservesColimit_toFunctor [sSupHomClass F α β] {J : Type w} [Category.{w'} J] + (K : J ⥤ α) : PreservesColimit K (toOrderHom f).toFunctor := + preservesColimit_of_preserves_colimit_cocone (colimitCocone K).isColimit <| + (colimitCocone _).isColimit.ofIsoColimit (Cocone.ext (eqToIso (by aesop)) (by subsingleton)) + +instance preservesColimitsOfShape_toFunctor [sSupHomClass F α β] {J : Type w} [Category.{w'} J] : + PreservesColimitsOfShape J (toOrderHom f).toFunctor where + +instance preservesColimitsOfSize_toFunctor [sSupHomClass F α β] : + PreservesColimitsOfSize.{w', w} (toOrderHom f).toFunctor where + +instance preservesColimits_toFunctor [sSupHomClass F α β] : + PreservesColimits (toOrderHom f).toFunctor where + +end + +end CategoryTheory.Limits.CompleteLattice diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean b/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean index d81f695d3dd635..55a57cc5349fce 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Grothendieck.lean @@ -178,7 +178,7 @@ def isColimitCoconeOfFiberwiseCocone {c : Cocone (fiberwiseColimit G)} (hc : IsC desc s := hc.desc <| Cocone.mk s.pt <| { app := fun X => colimit.desc (Grothendieck.ι F X ⋙ G) (s.whisker _) } uniq s m hm := hc.hom_ext <| fun X => by - simp only [fiberwiseColimit_obj, Functor.const_obj_obj, IsColimit.fac] + simp only [fiberwiseColimit_obj, IsColimit.fac] simp only [coconeOfCoconeFiberwiseColimit_pt, Functor.const_obj_obj, coconeOfCoconeFiberwiseColimit_ι_app, Category.assoc] at hm ext d diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Kernels.lean b/Mathlib/CategoryTheory/Limits/Shapes/Kernels.lean index 379087c149ca81..a76ecb17ce0a6b 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Kernels.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Kernels.lean @@ -474,12 +474,7 @@ def kernelIsIsoComp {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) [IsIso f] [HasKernel hom := kernel.lift _ (kernel.ι _ ≫ f) (by simp) inv := kernel.lift _ (kernel.ι _ ≫ inv f) (by simp) -set_option backward.defeqAttrib.useBackward true in -/-- Equal maps have isomorphic kernels. -/ -@[simps] def kernel.congr {X Y : C} (f g : X ⟶ Y) [HasKernel f] [HasKernel g] - (h : f = g) : kernel f ≅ kernel g where - hom := kernel.lift _ (kernel.ι f) (by simp [← h]) - inv := kernel.lift _ (kernel.ι g) (by simp [h]) +@[deprecated (since := "2026-07-03")] alias kernel.congr := kernelIsoOfEq lemma isZero_kernel_of_mono {X Y : C} (f : X ⟶ Y) [Mono f] [HasKernel f] : IsZero (kernel f) := @@ -579,11 +574,9 @@ end Transport section -variable (X Y) - /-- The kernel morphism of a zero morphism is an isomorphism -/ -theorem kernel.ι_of_zero : IsIso (kernel.ι (0 : X ⟶ Y)) := - equalizer.ι_of_self _ +theorem kernel.ι_of_zero {f : X ⟶ Y} [HasKernel f] (eq : f = 0) : + IsIso (kernel.ι f) := equalizer.ι_of_eq eq end @@ -1007,12 +1000,7 @@ def cokernelEpiComp {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) [Epi f] [HasCokernel rw [← cancel_epi f, ← Category.assoc] simp) -set_option backward.defeqAttrib.useBackward true in -/-- Equal maps have isomorphic cokernels. -/ -@[simps] def cokernel.congr {X Y : C} (f g : X ⟶ Y) [HasCokernel f] [HasCokernel g] - (h : f = g) : cokernel f ≅ cokernel g where - hom := cokernel.desc _ (cokernel.π g) (by simp [h]) - inv := cokernel.desc _ (cokernel.π f) (by simp [← h]) +@[deprecated (since := "2026-07-03")] alias cokernel.congr := cokernelIsoOfEq lemma isZero_cokernel_of_epi {X Y : C} (f : X ⟶ Y) [Epi f] [HasCokernel f] : IsZero (cokernel f) := @@ -1100,7 +1088,7 @@ variable (f : X ⟶ Y) [HasKernel f] [HasImage f] [HasKernel (factorThruImage f) /-- The kernel of the morphism `X ⟶ image f` is just the kernel of `f`. -/ def kernelFactorThruImage : kernel (factorThruImage f) ≅ kernel f := - (kernelCompMono (factorThruImage f) (image.ι f)).symm ≪≫ (kernel.congr _ _ (by simp)) + (kernelCompMono (factorThruImage f) (image.ι f)).symm ≪≫ (kernelIsoOfEq (by simp)) @[reassoc (attr := simp)] theorem kernelFactorThruImage_hom_comp_ι : @@ -1118,11 +1106,9 @@ end HasImage section -variable (X Y) - /-- The cokernel of a zero morphism is an isomorphism -/ -theorem cokernel.π_of_zero : IsIso (cokernel.π (0 : X ⟶ Y)) := - coequalizer.π_of_self _ +theorem cokernel.π_of_zero {f : X ⟶ Y} [HasCokernel f] (eq : f = 0) : + IsIso (cokernel.π f) := coequalizer.π_of_eq eq end @@ -1328,4 +1314,5 @@ set_option backward.defeqAttrib.useBackward true in @[reassoc (attr := simp)] lemma coker.condition : Arrow.leftToRight ≫ π C = 0 := by cat_disch end HasCokernels + end CategoryTheory.Limits diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean index aaef3ca24fd17d..4ea64dd9d69f3e 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Multiequalizer.lean @@ -35,9 +35,9 @@ namespace CategoryTheory.Limits universe t w w' v u +set_option linter.checkUnivs false in /-- The shape of a multiequalizer diagram. It involves two types `L` and `R`, and two maps `R → L`. -/ -@[nolint checkUnivs] structure MulticospanShape where /-- the left type -/ L : Type w @@ -58,9 +58,9 @@ def MulticospanShape.prod (ι : Type w) : MulticospanShape where fst := _root_.Prod.fst snd := _root_.Prod.snd +set_option linter.checkUnivs false in /-- The shape of a multicoequalizer diagram. It involves two types `L` and `R`, and two maps `L → R`. -/ -@[nolint checkUnivs] structure MultispanShape where /-- the left type -/ L : Type w @@ -298,7 +298,6 @@ def arrowEquiv : end WalkingMultispan /-- This is a structure encapsulating the data necessary to define a `Multicospan`. -/ -@[nolint checkUnivs] structure MulticospanIndex (J : MulticospanShape.{w, w'}) (C : Type u) [Category.{v} C] where /-- Left map, from `J.L` to `C` -/ @@ -311,7 +310,6 @@ structure MulticospanIndex (J : MulticospanShape.{w, w'}) snd : ∀ b, left (J.snd b) ⟶ right b /-- This is a structure encapsulating the data necessary to define a `Multispan`. -/ -@[nolint checkUnivs] structure MultispanIndex (J : MultispanShape.{w, w'}) (C : Type u) [Category.{v} C] where /-- Left map, from `J.L` to `C` -/ @@ -691,28 +689,11 @@ def ofPiFork | WalkingMulticospan.left _ => a.ι ≫ c.proj _ | WalkingMulticospan.right _ => a.ι ≫ I.fstPiMapOfIsLimit c hd ≫ d.proj _ π.naturality := by - #adaptation_note /-- Proof repaired after leanprover/lean4#13363. - The proof used to finish from this point as - ``` rintro (_ | _) (_ | _) (_ | _ | _) · simp · simp · dsimp; rw [a.condition_assoc]; simp · simp - ``` - The replacement proof is a short-term fix, and we request that the authors/maintainers of - this file review the proof, and either approve it by removing this note, revise - the proof or the prerequisites appropriately, or minimize a problem in lean4 that still - needs addressing. -/ - rintro (_ | _) (_ | _) (_ | _ | _) - · simp only [WalkingMulticospan.Hom.id_eq_id, Functor.map_id, - Functor.const_obj_map, Category.comp_id] - exact Category.id_comp _ - · simp - · dsimp; rw [a.condition_assoc]; simp - · simp only [WalkingMulticospan.Hom.id_eq_id, Functor.map_id, - Functor.const_obj_map, Category.comp_id] - exact Category.id_comp _ @[simp] theorem ofPiFork_ι (a : Fork (I.fstPiMapOfIsLimit c hd) (I.sndPiMapOfIsLimit c hd)) (i) : diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Opposites/Equalizers.lean b/Mathlib/CategoryTheory/Limits/Shapes/Opposites/Equalizers.lean index dbd325de31bf8a..fee3e3f67f5143 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Opposites/Equalizers.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Opposites/Equalizers.lean @@ -108,8 +108,8 @@ namespace Cofork /-- The obvious map `Cofork f g → Fork f.unop g.unop` -/ def unop {X Y : Cᵒᵖ} {f g : X ⟶ Y} (c : Cofork f g) : Fork f.unop g.unop := - Cocone.unop ((Cocone.precompose (opParallelPairIso f.unop g.unop).hom).obj - (Cocone.whisker walkingParallelPairOpEquiv.inverse c)) + Cocone.unop ((Cocone.precompose (opParallelPairIso f.unop g.unop).hom).obj + (Cocone.whisker walkingParallelPairOpEquiv.inverse c)) set_option backward.defeqAttrib.useBackward true in lemma unop_π_app_one {X Y : Cᵒᵖ} {f g : X ⟶ Y} (c : Cofork f g) : diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Products.lean b/Mathlib/CategoryTheory/Limits/Shapes/Products.lean index 8d0db515c200f9..e7c71c245c1e4f 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Products.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Products.lean @@ -826,10 +826,21 @@ instance (priority := 100) hasProduct_unique [Nonempty β] [Subsingleton β] (f let ⟨_⟩ := nonempty_unique β; HasLimit.mk (limitConeOfUnique f) /-- A product over an index type with exactly one term is just the object over that term. -/ -@[simps!] def productUniqueIso [Unique β] (f : β → C) : ∏ᶜ f ≅ f default := IsLimit.conePointUniqueUpToIso (limit.isLimit _) (limitConeOfUnique f).isLimit +@[simp] +lemma productUniqueIso_hom [Unique β] (f : β → C) : (productUniqueIso f).hom = Pi.π f default := + rfl + +@[reassoc (attr := simp)] +lemma productUniqueIso_inv_π [Unique β] (f : β → C) (b : β) : + (productUniqueIso f).inv ≫ Pi.π f b = eqToHom (congrArg _ <| Subsingleton.allEq _ _) := by + obtain rfl := Subsingleton.allEq b default + simp [Iso.inv_comp_eq] + +@[deprecated (since := "2026-06-30")] alias productUniqueIso_inv := productUniqueIso_inv_π + set_option backward.defeqAttrib.useBackward true in /-- Any isomorphism is the projection from a single object product. -/ def Fan.isLimitMkOfUnique {X Y : C} (e : X ≅ Y) (J : Type*) [Unique J] : @@ -863,10 +874,23 @@ instance (priority := 100) hasCoproduct_unique [Nonempty β] [Subsingleton β] ( let ⟨_⟩ := nonempty_unique β; HasColimit.mk (colimitCoconeOfUnique f) /-- A coproduct over an index type with exactly one term is just the object over that term. -/ -@[simps!] def coproductUniqueIso [Unique β] (f : β → C) : ∐ f ≅ f default := IsColimit.coconePointUniqueUpToIso (colimit.isColimit _) (colimitCoconeOfUnique f).isColimit +@[simp] +lemma coproductUniqueIso_inv [Unique β] (f : β → C) : + (coproductUniqueIso f).inv = Sigma.ι f default := + rfl + +@[reassoc (attr := simp)] +lemma ι_coproductUniqueIso_hom [Unique β] (f : β → C) (b : β) : + Sigma.ι f b ≫ (coproductUniqueIso f).hom = eqToHom (congrArg _ <| Subsingleton.allEq _ _) := by + obtain rfl := Subsingleton.allEq b default + symm + simp [← Iso.comp_inv_eq] + +@[deprecated (since := "2026-06-30")] alias coproductUniqueIso_hom := ι_coproductUniqueIso_hom + set_option backward.defeqAttrib.useBackward true in /-- Any isomorphism is the projection from a single object product. -/ def Cofan.isColimitMkOfUnique {X Y : C} (e : X ≅ Y) (J : Type*) [Unique J] : @@ -967,6 +991,27 @@ instance [HasColimit F] [HasCoproduct F.obj] : Epi (Sigma.desc (colimit.ι F)) w end +section Thin + +variable [Quiver.IsThin C] {J : Type*} [Category* J] {K : J ⥤ C} + +/-- If `K : J ⥤ C` is a diagram with `C` thin, a cone for `K` is limiting +if and only if the cone point is the product of the components. -/ +def isLimitEquivFanOfIsThin (c : Cone K) : IsLimit c ≃ IsLimit (Fan.mk c.pt c.π.app) where + toFun hc := Fan.IsLimit.mk _ (fun s ↦ hc.lift { pt := s.pt, π.app j := s.proj j }) + (by subsingleton) (by subsingleton) + invFun h := { lift s := Fan.IsLimit.lift h s.π.app } + +/-- If `K : J ⥤ C` is a diagram with `C` thin, a cone for `K` is limiting +if and only if the cone point is the product of the components. -/ +def isColimitEquivCofanOfIsThin (c : Cocone K) : + IsColimit c ≃ IsColimit (Cofan.mk c.pt c.ι.app) where + toFun hc := Cofan.IsColimit.mk _ (fun s ↦ hc.desc { pt := s.pt, ι.app j := s.inj j }) + (by subsingleton) (by subsingleton) + invFun h := { desc s := Cofan.IsColimit.desc h s.ι.app } + +end Thin + section Fubini variable {ι ι' : Type*} {X : ι → ι' → C} diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean index 3cf75a355296f5..e2e5e749c297f5 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/HasPullback.lean @@ -9,6 +9,7 @@ public import Mathlib.CategoryTheory.Limits.Shapes.Pullback.PullbackCone /-! # HasPullback + `HasPullback f g` and `pullback f g` provides API for `HasLimit` and `limit` in the case of pullbacks. @@ -194,16 +195,18 @@ theorem pushout.inr_desc {W X Y Z : C} {f : X ⟶ Y} {g : X ⟶ Z} [HasPushout f `l : W ⟶ pullback f g` such that `l ≫ pullback.fst = h` and `l ≫ pullback.snd = k`. -/ def pullback.lift' {W X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} [HasPullback f g] (h : W ⟶ X) (k : W ⟶ Y) (w : h ≫ f = k ≫ g) : - { l : W ⟶ pullback f g // l ≫ pullback.fst f g = h ∧ l ≫ pullback.snd f g = k } := + { l : W ⟶ pullback f g // l ≫ pullback.fst f g = h ∧ l ≫ pullback.snd f g = k } := ⟨pullback.lift h k w, pullback.lift_fst _ _ _, pullback.lift_snd _ _ _⟩ /-- A pair of morphisms `h : Y ⟶ W` and `k : Z ⟶ W` satisfying `f ≫ h = g ≫ k` induces a morphism `l : pushout f g ⟶ W` such that `pushout.inl _ _ ≫ l = h` and `pushout.inr _ _ ≫ l = k`. -/ -def pullback.desc' {W X Y Z : C} {f : X ⟶ Y} {g : X ⟶ Z} [HasPushout f g] (h : Y ⟶ W) (k : Z ⟶ W) +def pushout.desc' {W X Y Z : C} {f : X ⟶ Y} {g : X ⟶ Z} [HasPushout f g] (h : Y ⟶ W) (k : Z ⟶ W) (w : f ≫ h = g ≫ k) : - { l : pushout f g ⟶ W // pushout.inl _ _ ≫ l = h ∧ pushout.inr _ _ ≫ l = k } := + { l : pushout f g ⟶ W // pushout.inl _ _ ≫ l = h ∧ pushout.inr _ _ ≫ l = k } := ⟨pushout.desc h k w, pushout.inl_desc _ _ _, pushout.inr_desc _ _ _⟩ +@[deprecated (since := "2026-06-25")] alias pullback.desc' := pushout.desc' + @[reassoc] theorem pullback.condition {X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} [HasPullback f g] : pullback.fst f g ≫ f = pullback.snd f g ≫ g := diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean index fc8dafa3356779..842a249e0832c2 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/IsPullback/Basic.lean @@ -943,6 +943,54 @@ lemma IsPushout.iff_app [HasPushouts D] {F₁ F₂ F₃ F₄ : C ⥤ D} end Functor +section Thin + +variable [Quiver.IsThin C] + +lemma isPullback_iff_isLimit_binaryFan_of_isThin {P X Y Z : C} + {fst : P ⟶ X} {snd : P ⟶ Y} {f : X ⟶ Z} {g : Y ⟶ Z} : + IsPullback fst snd f g ↔ Nonempty (IsLimit (BinaryFan.mk fst snd)) := by + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · exact ⟨BinaryFan.IsLimit.mk _ (fun u v ↦ h.lift u v (by subsingleton)) + (by subsingleton) (by subsingleton) (by subsingleton)⟩ + · exact ⟨⟨by subsingleton⟩, + ⟨PullbackCone.IsLimit.mk _ (fun s ↦ BinaryFan.IsLimit.lift h.some s.fst s.snd) + (by subsingleton) (by subsingleton) (by subsingleton)⟩⟩ + +lemma isPushout_iff_isColimit_binaryCofan_of_isThin {P X Y Z : C} + {f : Z ⟶ X} {g : Z ⟶ Y} {inl : X ⟶ P} {inr : Y ⟶ P} : + IsPushout f g inl inr ↔ Nonempty (IsColimit (BinaryCofan.mk inl inr)) := by + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · exact ⟨BinaryCofan.IsColimit.mk _ (fun u v ↦ h.desc u v (by subsingleton)) + (by subsingleton) (by subsingleton) (by subsingleton)⟩ + · exact ⟨⟨by subsingleton⟩, + ⟨PushoutCocone.IsColimit.mk _ (fun s ↦ BinaryCofan.IsColimit.desc h.some s.inl s.inr) + (by subsingleton) (by subsingleton) (by subsingleton)⟩⟩ + +variable {D : Type*} [Category* D] [Quiver.IsThin D] (F : C ⥤ D) + +instance (priority := low) [PreservesLimitsOfShape (Discrete WalkingPair) F] : + PreservesLimitsOfShape WalkingCospan F := by + refine preservesLimitsOfShape_walkingCospan_of_forall_isPullback fun X Y Z f g hfg ↦ ?_ + use pullback f g, pullback.fst f g, pullback.snd f g, .of_hasPullback f g + rw [isPullback_iff_isLimit_binaryFan_of_isThin] + refine ⟨(BinaryFan.mk (pullback.fst f g) (pullback.snd f g)).isLimitMapConeEquiv ?_⟩ + apply isLimitOfPreserves _ (Nonempty.some ?_) + rw [← CategoryTheory.isPullback_iff_isLimit_binaryFan_of_isThin (f := f) (g := g)] + exact .of_hasPullback f g + +instance (priority := low) [PreservesColimitsOfShape (Discrete WalkingPair) F] : + PreservesColimitsOfShape WalkingSpan F := by + refine preservesColimitsOfShape_walkingCospan_of_forall_isPushout fun X Y Z f g hfg ↦ ?_ + use pushout f g, pushout.inl f g, pushout.inr f g, .of_hasPushout f g + rw [isPushout_iff_isColimit_binaryCofan_of_isThin] + refine ⟨(BinaryCofan.mk (pushout.inl f g) (pushout.inr f g)).isColimitMapConeEquiv ?_⟩ + apply isColimitOfPreserves _ (Nonempty.some ?_) + rw [← CategoryTheory.isPushout_iff_isColimit_binaryCofan_of_isThin (f := f) (g := g)] + exact .of_hasPushout f g + +end Thin + section IsPullbackOverPullback open Limits @@ -994,4 +1042,54 @@ end IsPullback end IsPullbackOverPullback +namespace Limits + +instance {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) {X' : C} (i : X' ⟶ X) [IsIso i] [HasPullback f g] : + HasPullback (i ≫ f) g := + IsPullback.paste_vert + (IsPullback.of_vert_isIso_mono (fst := pullback.fst _ _ ≫ inv i) (snd := 𝟙 (pullback f g)) <| + ⟨by simp⟩) (.of_hasPullback f g) |>.hasPullback + +@[simp] +lemma HasPullback.comp_left_left_iff_of_isIso + {X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} {X' : C} (i : X' ⟶ X) [IsIso i] : + HasPullback (i ≫ f) g ↔ HasPullback f g := by + refine ⟨fun h ↦ ?_, fun _ ↦ inferInstance⟩ + rw [← IsIso.inv_hom_id_assoc i f] + infer_instance + +instance {X Y Z Z' : C} {f : X ⟶ Z} {g : Y ⟶ Z'} (i : Z ⟶ Z') [IsIso i] [HasPullback (f ≫ i) g] : + HasPullback f (g ≫ inv i) := by + simpa using hasPullback_of_comp_mono (f ≫ i) g (inv i) + +lemma HasPullback.comp_left_right_iff_of_isIso + {X Y Z Z' : C} {f : X ⟶ Z} {g : Y ⟶ Z'} (i : Z ⟶ Z') [IsIso i] : + HasPullback (f ≫ i) g ↔ HasPullback f (g ≫ inv i) := + ⟨fun h ↦ inferInstance, fun h ↦ by simpa using hasPullback_of_comp_mono f (g ≫ inv i) i⟩ + +instance {X Y Z : C} (f : Z ⟶ X) (g : Z ⟶ Y) {X' : C} (i : X ⟶ X') [IsIso i] [HasPushout f g] : + HasPushout (f ≫ i) g := + IsPushout.paste_horiz (.of_hasPushout f g) + (IsPushout.of_horiz_isIso_epi (inl := inv i ≫ pushout.inl _ _) (inr := 𝟙 (pushout f g)) <| + ⟨by simp⟩) |>.hasPushout + +@[simp] +lemma HasPushout.comp_left_left_iff_of_isIso + {X Y Z : C} {f : Z ⟶ X} {g : Z ⟶ Y} {X' : C} (i : X ⟶ X') [IsIso i] : + HasPushout (f ≫ i) g ↔ HasPushout f g := by + refine ⟨fun h ↦ ?_, fun _ ↦ inferInstance⟩ + rw [← Category.comp_id f, ← IsIso.hom_inv_id i, ← Category.assoc] + infer_instance + +instance {X Y Z Z' : C} {f : Z ⟶ X} {g : Z' ⟶ Y} (i : Z' ⟶ Z) [IsIso i] [HasPushout (i ≫ f) g] : + HasPushout f (inv i ≫ g) := by + simpa using hasPushout_of_epi_comp (i ≫ f) g (inv i) + +lemma HasPushout.comp_left_right_iff_of_isIso + {X Y Z Z' : C} {f : Z ⟶ X} {g : Z' ⟶ Y} (i : Z' ⟶ Z) [IsIso i] : + HasPushout (i ≫ f) g ↔ HasPushout f (inv i ≫ g) := + ⟨fun h ↦ inferInstance, fun h ↦ by simpa using hasPushout_of_epi_comp f (inv i ≫ g) i⟩ + +end Limits + end CategoryTheory diff --git a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/PullbackObjObj.lean b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/PullbackObjObj.lean index 240a41e7591ce0..d6dffd0357608d 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/Pullback/PullbackObjObj.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/Pullback/PullbackObjObj.lean @@ -78,11 +78,17 @@ structure PushoutObjObj where /-- the second inclusion -/ inr : (F.obj X₁).obj Y₂ ⟶ pt isPushout : IsPushout ((F.map f₁).app X₂) ((F.obj X₁).map f₂) inl inr + /-- the Leibniz pushout -/ + ι : pt ⟶ (F.obj Y₁).obj Y₂ := isPushout.desc ((F.obj Y₁).map f₂) ((F.map f₁).app Y₂) (by simp) + inl_ι : inl ≫ ι = (F.obj Y₁).map f₂ := by cat_disch + inr_ι : inr ≫ ι = (F.map f₁).app Y₂ := by cat_disch namespace PushoutObjObj +attribute [reassoc (attr := simp)] inl_ι inr_ι + /-- The `PushoutObjObj` structure given by the pushout of the colimits API. -/ -@[simps -isSimp] +@[simps] noncomputable def ofHasPushout [HasPushout ((F.map f₁).app X₂) ((F.obj X₁).map f₂)] : F.PushoutObjObj f₁ f₂ where @@ -90,25 +96,18 @@ noncomputable def ofHasPushout inl := pushout.inl _ _ inr := pushout.inr _ _ isPushout := IsPushout.of_hasPushout _ _ + ι := pushout.desc ((F.obj Y₁).map f₂) ((F.map f₁).app Y₂) (by simp) + inl_ι := pushout.inl_desc .. + inr_ι := pushout.inr_desc .. 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 [ι] - @[ext] lemma hom_ext {X₃ : C₃} {f g : sq.pt ⟶ X₃} (hₗ : sq.inl ≫ f = sq.inl ≫ g) (hᵣ : sq.inr ≫ f = sq.inr ≫ g) : f = g := sq.isPushout.hom_ext hₗ hᵣ +set_option backward.defeqAttrib.useBackward true in /-- Given `sq : F.PushoutObjObj f₁ f₂`, flipping the pushout square gives `sq.flip : F.flip.PushoutObjObj f₂ f₁`. -/ @[simps] @@ -117,13 +116,9 @@ def flip : F.flip.PushoutObjObj f₂ f₁ where inl := sq.inr inr := sq.inl isPushout := sq.isPushout.flip + ι := sq.ι -set_option backward.isDefEq.respectTransparency false in -@[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] +@[deprecated (since := "2026-06-19")] alias ι_flip := flip_ι section @@ -138,23 +133,10 @@ def ofNatIso : F'.PushoutObjObj f₁ f₂ where isPushout := sq.isPushout.of_iso ((e.app _).app _) ((e.app _).app _) ((e.app _).app _) (Iso.refl _) (by simp) (by simp) (by simp) (by simp) - -set_option backward.defeqAttrib.useBackward true in -@[simp, reassoc] -lemma ofNatIso_ι : - (sq.ofNatIso e).ι = sq.ι ≫ (e.hom.app _).app _ := by - apply sq.hom_ext - · simp [← (sq.ofNatIso e).inl_ι] - · simp [← (sq.ofNatIso e).inr_ι] + ι := sq.ι ≫ (e.hom.app _).app _ end -set_option backward.isDefEq.respectTransparency false in -lemma ofHasPushout_ι [HasPushout ((F.map f₁).app X₂) ((F.obj X₁).map f₂)] : - (ofHasPushout F f₁ f₂).ι = - pushout.desc ((F.obj Y₁).map f₂) ((F.map f₁).app Y₂) (by simp) := by - ext <;> simp [PushoutObjObj.ι, ofHasPushout_inl, ofHasPushout_inr] - section variable (F f₁ f₂) @@ -162,6 +144,7 @@ variable (F f₁ f₂) [PreservesColimitsOfShape (Discrete PEmpty.{1}) (F.flip.obj Y₂)] (h : IsInitial X₁) +set_option backward.defeqAttrib.useBackward true in /-- A `Functor.PushoutObjObj` structure for a functor `F : C₁ ⥤ C₂ ⥤ C₃` and morphisms `f₁ : X₁ ⟶ Y₁` and `f₂ : X₂ ⟶ Y₂` when `X₁` is initial and both `F.flip.obj X₂` and `F.flip.obj Y₂` preserve the initial object. -/ @@ -176,11 +159,8 @@ noncomputable def ofIsInitialLeft : F.PushoutObjObj f₁ f₂ where apply +allowSynthFailures IsPushout.of_vert_isIso · exact isIso_of_isInitial hX₂ hY₂ _ · exact ⟨hX₂.hom_ext _ _⟩ - -set_option backward.defeqAttrib.useBackward true in -@[simp] -lemma ofIsInitialLeft_ι : (ofIsInitialLeft F f₁ f₂ h).ι = (F.obj Y₁).map f₂ := by - simpa using (ofIsInitialLeft F f₁ f₂ h).inl_ι + ι := (F.obj Y₁).map f₂ + inr_ι := (IsInitial.isInitialObj (F.flip.obj Y₂) _ h).hom_ext .. end @@ -205,11 +185,8 @@ noncomputable def ofIsInitialRight : F.PushoutObjObj f₁ f₂ where apply +allowSynthFailures IsPushout.of_horiz_isIso · exact isIso_of_isInitial hX₁ hY₁ _ · exact ⟨hX₁.hom_ext _ _⟩ - -set_option backward.defeqAttrib.useBackward true in -@[simp] -lemma ofIsInitialRight_ι : (ofIsInitialRight F f₁ f₂ h).ι = (F.map f₁).app Y₂ := by - simpa using (ofIsInitialRight F f₁ f₂ h).inr_ι + ι := (F.map f₁).app Y₂ + inl_ι := (IsInitial.isInitialObj (F.obj Y₁) _ h).hom_ext .. end @@ -360,11 +337,19 @@ structure PullbackObjObj where snd : pt ⟶ (G.obj (op Y₁)).obj Y₃ isPullback : IsPullback fst snd ((G.obj (op X₁)).map f₃) ((G.map f₁.op).app Y₃) + /-- the Leibniz pullback -/ + π : (G.obj (op Y₁)).obj X₃ ⟶ pt := + isPullback.lift ((G.map f₁.op).app X₃) ((G.obj (op Y₁)).map f₃) (by simp) + π_fst : π ≫ fst = (G.map f₁.op).app X₃ := by cat_disch + π_snd : π ≫ snd = (G.obj (op Y₁)).map f₃ := by cat_disch namespace PullbackObjObj +attribute [reassoc (attr := simp)] π_fst π_snd + +set_option backward.isDefEq.respectTransparency false in /-- The `PullbackObjObj` structure given by the pullback of the limits API. -/ -@[simps -isSimp] +@[simps] noncomputable def ofHasPullback [HasPullback ((G.obj (op X₁)).map f₃) ((G.map f₁.op).app Y₃)] : G.PullbackObjObj f₁ f₃ where @@ -372,32 +357,15 @@ noncomputable def ofHasPullback fst := pullback.fst _ _ snd := pullback.snd _ _ isPullback := IsPullback.of_hasPullback _ _ + π := pullback.lift ((G.map f₁.op).app X₃) ((G.obj (op Y₁)).map f₃) (by simp) 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 [π] - @[ext] lemma hom_ext {X₂ : C₂} {f g : X₂ ⟶ sq.pt} (h₁ : f ≫ sq.fst = g ≫ sq.fst) (h₂ : f ≫ sq.snd = g ≫ sq.snd) : f = g := sq.isPullback.hom_ext h₁ h₂ -set_option backward.isDefEq.respectTransparency false in -lemma ofHasPullback_π - [HasPullback ((G.obj (op X₁)).map f₃) ((G.map f₁.op).app Y₃)] : - (ofHasPullback G f₁ f₃).π = - pullback.lift ((G.map f₁.op).app X₃) ((G.obj (op Y₁)).map f₃) (by simp) := by - ext <;> simp [PullbackObjObj.π, ofHasPullback_fst, ofHasPullback_snd] - section variable (G f₁ f₃) @@ -419,11 +387,8 @@ noncomputable def ofIsInitial : G.PullbackObjObj f₁ f₃ where apply +allowSynthFailures IsPullback.of_vert_isIso · exact isIso_of_isTerminal hX₃ hY₃ _ · exact ⟨hY₃.hom_ext _ _⟩ - -set_option backward.defeqAttrib.useBackward true in -@[simp] -lemma ofIsInitial_π : (ofIsInitial G f₁ f₃ h).π = (G.obj (op Y₁)).map f₃ := by - simpa using (ofIsInitial G f₁ f₃ h).π_snd + π := (G.obj (op Y₁)).map f₃ + π_fst := (IsTerminal.isTerminalObj (G.flip.obj X₃) _ h.op).hom_ext _ _ end @@ -448,11 +413,8 @@ noncomputable def ofIsTerminal : G.PullbackObjObj f₁ f₃ where apply +allowSynthFailures IsPullback.of_horiz_isIso · exact isIso_of_isTerminal hY₁ hX₁ _ · exact ⟨hX₁.hom_ext _ _⟩ - -set_option backward.defeqAttrib.useBackward true in -@[simp] -lemma ofIsTerminal_π : (ofIsTerminal G f₁ f₃ h).π = (G.map f₁.op).app X₃ := by - simpa using (ofIsTerminal G f₁ f₃ h).π_fst + π := (G.map f₁.op).app X₃ + π_snd := (IsTerminal.isTerminalObj (G.obj (op Y₁)) _ h).hom_ext _ _ end diff --git a/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean b/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean index 9c6a0fcccc6428..e9605bfa71774f 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/SplitEqualizer.lean @@ -120,7 +120,7 @@ def IsSplitEqualizer.isEqualizer {W : C} {h : W ⟶ X} (t : IsSplitEqualizer f g IsLimit t.asFork := Fork.IsLimit.mk' _ fun s => ⟨ s.ι ≫ t.leftRetraction, - by simp [- top_rightRetraction, ← t.top_rightRetraction, s.condition_assoc], + by simp [-top_rightRetraction, ← t.top_rightRetraction, s.condition_assoc], fun hm => by simp [← hm] ⟩ end diff --git a/Mathlib/CategoryTheory/Limits/Shapes/StrictInitial.lean b/Mathlib/CategoryTheory/Limits/Shapes/StrictInitial.lean index a6b167a4344f0d..4b0713b479ab5b 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/StrictInitial.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/StrictInitial.lean @@ -156,6 +156,11 @@ theorem hasStrictInitialObjects_of_initial_is_strict [HasInitial C] haveI := h A (f ≫ hI.to _) ⟨⟨hI.to _ ≫ inv (f ≫ hI.to (⊥_ C)), by rw [← assoc, IsIso.hom_inv_id], hI.hom_ext _ _⟩⟩ } +instance [Quiver.IsThin C] : HasStrictInitialObjects C where + out {I A} f hI := by + rw [isIso_iff_of_thin] + exact ⟨hI.to _⟩ + end StrictInitial section StrictTerminal @@ -252,6 +257,11 @@ theorem hasStrictTerminalObjects_of_terminal_is_strict (I : C) (h : ∀ (A) (f : haveI := h A (hI'.from _ ≫ f) ⟨⟨inv (hI'.from I ≫ f) ≫ hI'.from I, hI'.hom_ext _ _, by rw [assoc, IsIso.inv_hom_id]⟩⟩ } +instance [Quiver.IsThin C] : HasStrictTerminalObjects C where + out {I A} f hI := by + rw [CategoryTheory.isIso_iff_of_thin] + exact ⟨hI.from _⟩ + end StrictTerminal end Limits diff --git a/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean b/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean index ccb8b9800f1a6c..e10bb4da61c91f 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/StrongEpi.lean @@ -45,6 +45,8 @@ namespace CategoryTheory variable {C : Type u} [Category.{v} C] variable {P Q : C} +to_dual_name_hint Epi Mono + /-- A strong epimorphism `f` is an epimorphism which has the left lifting property with respect to monomorphisms. -/ class StrongEpi (f : P ⟶ Q) : Prop where @@ -53,57 +55,41 @@ class StrongEpi (f : P ⟶ Q) : Prop where /-- The left lifting property with respect to all monomorphisms -/ llp : ∀ ⦃X Y : C⦄ (z : X ⟶ Y) [Mono z], HasLiftingProperty f z - -theorem StrongEpi.mk' {f : P ⟶ Q} [Epi f] - (hf : ∀ (X Y : C) (z : X ⟶ Y) - (_ : Mono z) (u : P ⟶ X) (v : Q ⟶ Y) (sq : CommSq u f z v), sq.HasLift) : - StrongEpi f := - { epi := inferInstance - llp := fun {X Y} z hz => ⟨fun {u v} sq => hf X Y z hz u v sq⟩ } - /-- A strong monomorphism `f` is a monomorphism which has the right lifting property with respect to epimorphisms. -/ +@[to_dual] class StrongMono (f : P ⟶ Q) : Prop where /-- The monomorphism condition on `f` -/ mono : Mono f /-- The right lifting property with respect to all epimorphisms -/ rlp : ∀ ⦃X Y : C⦄ (z : X ⟶ Y) [Epi z], HasLiftingProperty z f -theorem StrongMono.mk' {f : P ⟶ Q} [Mono f] - (hf : ∀ (X Y : C) (z : X ⟶ Y) (_ : Epi z) (u : X ⟶ P) - (v : Y ⟶ Q) (sq : CommSq u z f v), sq.HasLift) : StrongMono f where - mono := inferInstance - rlp := fun {X Y} z hz => ⟨fun {u v} sq => hf X Y z hz u v sq⟩ +attribute [to_dual existing] StrongEpi.llp StrongEpi.mk -attribute [instance 100] StrongEpi.llp - -attribute [instance 100] StrongMono.rlp - -instance (priority := 100) epi_of_strongEpi (f : P ⟶ Q) [StrongEpi f] : Epi f := - StrongEpi.epi +@[to_dual (reorder := hf (X Y, u v))] +theorem StrongEpi.mk' {f : P ⟶ Q} [Epi f] + (hf : ∀ (X Y : C) (z : X ⟶ Y) (_ : Mono z) (u : P ⟶ X) + (v : Q ⟶ Y) (sq : CommSq u f z v), sq.HasLift) : StrongEpi f where + epi := inferInstance + llp {X Y} z hz := ⟨fun {u v} sq => hf X Y z hz u v sq⟩ -instance (priority := 100) mono_of_strongMono (f : P ⟶ Q) [StrongMono f] : Mono f := - StrongMono.mono +attribute [instance 100] StrongEpi.epi StrongEpi.llp StrongMono.mono StrongMono.rlp section variable {R : C} (f : P ⟶ Q) (g : Q ⟶ R) /-- The composition of two strong epimorphisms is a strong epimorphism. -/ +@[to_dual /-- The composition of two strong monomorphisms is a strong monomorphism. -/] instance strongEpi_comp [StrongEpi f] [StrongEpi g] : StrongEpi (f ≫ g) := { epi := epi_comp _ _ llp := by intros infer_instance } -/-- The composition of two strong monomorphisms is a strong monomorphism. -/ -instance strongMono_comp [StrongMono f] [StrongMono g] : StrongMono (f ≫ g) := - { mono := mono_comp _ _ - rlp := by - intros - infer_instance } - /-- If `f ≫ g` is a strong epimorphism, then so is `g`. -/ +@[to_dual (reorder := f g) (rename := f ↔ g, P ↔ R) +/-- If `f ≫ g` is a strong monomorphism, then so is `f`. -/] theorem strongEpi_of_strongEpi [StrongEpi (f ≫ g)] : StrongEpi g := { epi := epi_of_epi f g llp := fun {X Y} z _ => by @@ -115,67 +101,36 @@ theorem strongEpi_of_strongEpi [StrongEpi (f ≫ g)] : StrongEpi g := ⟨(CommSq.mk h₀).lift, by simp only [← cancel_mono z, Category.assoc, CommSq.fac_right, sq.w], by simp⟩ } -/-- If `f ≫ g` is a strong monomorphism, then so is `f`. -/ -theorem strongMono_of_strongMono [StrongMono (f ≫ g)] : StrongMono f := - { mono := mono_of_mono f g - rlp := fun {X Y} z => by - intros - constructor - intro u v sq - have h₀ : u ≫ f ≫ g = z ≫ v ≫ g := by - rw [← Category.assoc, eq_whisker sq.w, Category.assoc] - exact CommSq.HasLift.mk' ⟨(CommSq.mk h₀).lift, by simp, by simp [← cancel_epi z, sq.w]⟩ } - /-- An isomorphism is in particular a strong epimorphism. -/ +@[to_dual /-- An isomorphism is in particular a strong monomorphism. -/] instance (priority := 100) strongEpi_of_isIso [IsIso f] : StrongEpi f where epi := by infer_instance llp {_ _} _ := HasLiftingProperty.of_left_iso _ _ -/-- An isomorphism is in particular a strong monomorphism. -/ -instance (priority := 100) strongMono_of_isIso [IsIso f] : StrongMono f where - mono := by infer_instance - rlp {_ _} _ := HasLiftingProperty.of_right_iso _ _ - set_option backward.isDefEq.respectTransparency false in +@[to_dual] theorem StrongEpi.of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'} - (e : Arrow.mk f ≅ Arrow.mk g) [h : StrongEpi f] : StrongEpi g := - { epi := by - rw [Arrow.iso_w' e] - infer_instance - llp := fun {X Y} z => by - intro - apply HasLiftingProperty.of_arrow_iso_left e z } - -set_option backward.isDefEq.respectTransparency false in -theorem StrongMono.of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'} - (e : Arrow.mk f ≅ Arrow.mk g) [h : StrongMono f] : StrongMono g := - { mono := by - rw [Arrow.iso_w' e] - infer_instance - rlp := fun {X Y} z => by - intro - apply HasLiftingProperty.of_arrow_iso_right z e } - + (e : Arrow.mk f ≅ Arrow.mk g) [h : StrongEpi f] : StrongEpi g where + epi := by + rw [Arrow.iso_w' e] + infer_instance + llp := fun {X Y} z => by + intro + apply HasLiftingProperty.of_arrow_iso_left e z + +@[to_dual] theorem StrongEpi.iff_of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'} (e : Arrow.mk f ≅ Arrow.mk g) : StrongEpi f ↔ StrongEpi g := by constructor <;> intro exacts [StrongEpi.of_arrow_iso e, StrongEpi.of_arrow_iso e.symm] -theorem StrongMono.iff_of_arrow_iso {A B A' B' : C} {f : A ⟶ B} {g : A' ⟶ B'} - (e : Arrow.mk f ≅ Arrow.mk g) : StrongMono f ↔ StrongMono g := by - constructor <;> intro - exacts [StrongMono.of_arrow_iso e, StrongMono.of_arrow_iso e.symm] - end /-- A strong epimorphism that is a monomorphism is an isomorphism. -/ +@[to_dual /-- A strong monomorphism that is an epimorphism is an isomorphism. -/] theorem isIso_of_mono_of_strongEpi (f : P ⟶ Q) [Mono f] [StrongEpi f] : IsIso f := ⟨⟨(CommSq.mk (show 𝟙 P ≫ f = f ≫ 𝟙 Q by simp)).lift, by simp⟩⟩ -/-- A strong monomorphism that is an epimorphism is an isomorphism. -/ -theorem isIso_of_epi_of_strongMono (f : P ⟶ Q) [Epi f] [StrongMono f] : IsIso f := - ⟨⟨(CommSq.mk (show 𝟙 P ≫ f = f ≫ 𝟙 Q by simp)).lift, by simp⟩⟩ - section variable (C) @@ -186,34 +141,27 @@ class StrongEpiCategory : Prop where strongEpi_of_epi : ∀ {X Y : C} (f : X ⟶ Y) [Epi f], StrongEpi f /-- A strong mono category is a category in which every monomorphism is strong. -/ +@[to_dual] class StrongMonoCategory : Prop where /-- A strong mono category is a category in which every monomorphism is strong. -/ strongMono_of_mono : ∀ {X Y : C} (f : X ⟶ Y) [Mono f], StrongMono f +attribute [to_dual existing] StrongEpiCategory.strongEpi_of_epi StrongEpiCategory.mk + end +@[to_dual] theorem strongEpi_of_epi [StrongEpiCategory C] (f : P ⟶ Q) [Epi f] : StrongEpi f := StrongEpiCategory.strongEpi_of_epi _ -theorem strongMono_of_mono [StrongMonoCategory C] (f : P ⟶ Q) [Mono f] : StrongMono f := - StrongMonoCategory.strongMono_of_mono _ - section attribute [local instance] strongEpi_of_epi +@[to_dual] instance (priority := 100) balanced_of_strongEpiCategory [StrongEpiCategory C] : Balanced C where isIso_of_mono_of_epi _ _ _ := isIso_of_mono_of_strongEpi _ end -section - -attribute [local instance] strongMono_of_mono - -instance (priority := 100) balanced_of_strongMonoCategory [StrongMonoCategory C] : Balanced C where - isIso_of_mono_of_epi _ _ _ := isIso_of_epi_of_strongMono _ - -end - end CategoryTheory diff --git a/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean b/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean index be97296ed55f29..19cba70e5635d5 100644 --- a/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean +++ b/Mathlib/CategoryTheory/Limits/Shapes/ZeroMorphisms.lean @@ -131,6 +131,10 @@ theorem zero_of_epi_comp {X Y Z : C} (f : X ⟶ Y) {g : Y ⟶ Z} [Epi f] (h : f rw [← comp_zero, cancel_epi] at h exact h +lemma comp_eq_zero_iff_of_epi {X Y Z : C} (f : X ⟶ Y) {g : Y ⟶ Z} [Epi f] : + f ≫ g = 0 ↔ g = 0 := + ⟨zero_of_epi_comp _, by simp +contextual⟩ + theorem eq_zero_of_image_eq_zero {X Y : C} {f : X ⟶ Y} [HasImage f] (w : image.ι f = 0) : f = 0 := by rw [← image.fac f, w, HasZeroMorphisms.comp_zero] diff --git a/Mathlib/CategoryTheory/Limits/Types/Coequalizers.lean b/Mathlib/CategoryTheory/Limits/Types/Coequalizers.lean index 1fca1ad2404cdb..f9625eedfb509b 100644 --- a/Mathlib/CategoryTheory/Limits/Types/Coequalizers.lean +++ b/Mathlib/CategoryTheory/Limits/Types/Coequalizers.lean @@ -65,7 +65,7 @@ theorem coequalizer_preimage_image_eq_of_preimage_eq (π : Y ⟶ Z) (e : f ≫ (mono_iff_injective (h.coconePointUniqueUpToIso (coequalizerColimit f g).isColimit).inv).mp inferInstance - refine (eqv.eqvGen_iff.mp (Relation.EqvGen.mono lem (Quot.eqvGen_exact ?_))).mp hy + refine (eqv.eqvGen_iff.mp (Relation.EqvGen.mono lem y _ (Quot.eqvGen_exact ?_))).mp hy apply e'' convert! e' · exact fun hx => ⟨_, hx, rfl⟩ diff --git a/Mathlib/CategoryTheory/Limits/Types/Colimits.lean b/Mathlib/CategoryTheory/Limits/Types/Colimits.lean index 544a0ffbce3a6b..ae469221a059ea 100644 --- a/Mathlib/CategoryTheory/Limits/Types/Colimits.lean +++ b/Mathlib/CategoryTheory/Limits/Types/Colimits.lean @@ -227,7 +227,7 @@ theorem jointly_surjective_of_isColimit {F : J ⥤ Type u} {t : Cocone F} (h : I (↾fun y ↦ ULift.up (y ≠ x))) · refine h.hom_ext fun j ↦ ?_ ext y - simp only [Functor.const_obj_obj, TypeCat.Fun.toFun_apply, comp_apply, hom_ofHom, + simp only [TypeCat.Fun.toFun_apply, comp_apply, hom_ofHom, TypeCat.Fun.coe_mk, ne_eq, true_iff] exact hx j y · intro he diff --git a/Mathlib/CategoryTheory/Limits/Types/Filtered.lean b/Mathlib/CategoryTheory/Limits/Types/Filtered.lean index 013c92339f0080..2d406b4c347a5b 100644 --- a/Mathlib/CategoryTheory/Limits/Types/Filtered.lean +++ b/Mathlib/CategoryTheory/Limits/Types/Filtered.lean @@ -117,7 +117,7 @@ protected theorem rel_eq_eqvGen_colimitTypeRel : constructor · apply eqvGen_colimitTypeRel_of_rel · rw [← (FilteredColimit.rel_equiv F).eqvGen_iff] - exact Relation.EqvGen.mono (rel_of_colimitTypeRel F) + exact Relation.EqvGen.mono (rel_of_colimitTypeRel F) _ _ set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in diff --git a/Mathlib/CategoryTheory/Limits/WeakLimits/Basic.lean b/Mathlib/CategoryTheory/Limits/WeakLimits/Basic.lean new file mode 100644 index 00000000000000..c071c30f351fd3 --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/WeakLimits/Basic.lean @@ -0,0 +1,259 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Limits.HasLimits + +/-! +# Weak limits + +If `F : J ⥤ C` is a functor and `c : Cone F`, we say that `c` is a weak limit of `F` if +every cone over `F` admits a (not necessarily unique) morphism to `c`. In other words, a +weak limit satisfies the same "versal property" as a limit, without the uniqueness +condition. In particular, weak limits are not unique, and they are not functorial. + +We set up some API for weak limits, mostly copied from that for limits, prove that any +limit cone is a weak limit cone, and that, if a limit exists, then it is a retract of any +weak limit (see `IsWeakLimit.retractOfIsLimit`). + +In the files `WeakEqualizers.lean`, `WeakKernels.lean` and `WeakPullbacks.lean`, we specialize +to weak equalizers, weak kernels and weak pullbacks, and give some API for those shapes, +again inspired from the non-weak case. We prove that a category with weak equalizers and +pullbacks has weak pullbacks, and that a preadditive category has weak equalizers if and only +if it has weak kernels. + +## References + +* [Peter J Freyd, *Representations in Abelian categories*, p. 99][freyd1966repabelian] + +-/ + +@[expose] public section + +noncomputable section + +open CategoryTheory Category Limits + +variable {J : Type*} [Category* J] {K : Type*} [Category* K] {C : Type*} + [Category* C] {F : Functor J C} {D : Type*} [Category* D] {G : Functor K D} + +namespace CategoryTheory.Limits + +/-- A cone `t` over `F` is a weak limit cone if each cone over `F` admits a +cone morphism to `t`. -/ +structure IsWeakLimit (t : Cone F) where + /-- There is a morphism from any cone point to `t.pt` -/ + lift : ∀ s : Cone F, s.pt ⟶ t.pt + /-- The map makes the triangle with the two natural transformations commute -/ + fac : ∀ (s : Cone F) (j : J), lift s ≫ t.π.app j = s.π.app j := by cat_disch + +attribute [reassoc (attr := simp)] IsWeakLimit.fac + +/-- +If `F` has a limit, then it is a retract of any weak limit of `F`. +-/ +def IsWeakLimit.retractOfIsLimit {t t' : Cone F} (l : IsLimit t) (l' : IsWeakLimit t') : + Retract t.pt t'.pt where + i := l'.lift t + r := l.lift t' + retract := l.hom_ext (fun _ ↦ by rw [assoc, id_comp, l.fac t', l'.fac t]) + +/-- +If `c : Cone F` is a limit, then it is a weak limit. +-/ +def IsLimit.isWeakLimit {t : Cone F} (l : IsLimit t) : IsWeakLimit t where + lift := l.lift + fac := l.fac + +/-- `WeakLimitCone F` contains a cone over `F` together with the information that it is +a weak limit. -/ +structure WeakLimitCone (F : J ⥤ C) where + /-- The cone itself -/ + cone : Cone F + /-- The proof that is the weak limit cone -/ + isWeakLimit : IsWeakLimit cone + +/-- +Any limit cone defines a weak limit cone with the same underlying cone over `F` and the same +lifts. +-/ +def WeakLimitCone.ofLimitCone {F : J ⥤ C} (c : LimitCone F) : WeakLimitCone F where + cone := c.cone + isWeakLimit := c.isLimit.isWeakLimit + +/-- `HasWeakLimit F` represents the mere existence of a weak limit for `F`. -/ +class HasWeakLimit (F : J ⥤ C) : Prop where mk' :: + /-- There is some weak limit cone for `F` -/ + exists_weakLimitCone : Nonempty (WeakLimitCone F) + +/-- +If `F` has a limit, then it has a weak limit. +-/ +instance (F : J ⥤ C) [HasLimit F] : HasWeakLimit F where + exists_weakLimitCone := Nonempty.intro (WeakLimitCone.ofLimitCone (getLimitCone F)) + +theorem HasWeakLimit.mk {F : J ⥤ C} (d : WeakLimitCone F) : HasWeakLimit F := + ⟨Nonempty.intro d⟩ + +/-- Use the axiom of choice to extract explicit `WeakLimitCone F` from `HasWeakLimit F`. -/ +@[no_expose] +def getWeakLimitCone (F : J ⥤ C) [HasWeakLimit F] : WeakLimitCone F := + Classical.choice <| HasWeakLimit.exists_weakLimitCone + +variable (J C) in +/-- `C` has weak limits of shape `J` if there exists a weak limit for every functor +`F : J ⥤ C`. -/ +class HasWeakLimitsOfShape : Prop where + /-- All functors `F : J ⥤ C` from `J` have weak limits -/ + hasWeakLimit : ∀ F : J ⥤ C, HasWeakLimit F := by infer_instance + +attribute [instance] HasWeakLimitsOfShape.hasWeakLimit + +instance (priority := 100) [HasLimitsOfShape J C] : HasWeakLimitsOfShape J C where + +-- Interface to the `HasWeakLimit` class. +/-- An arbitrary choice of weak limit cone for a functor. -/ +def weakLimit.cone (F : J ⥤ C) [HasWeakLimit F] : Cone F := + (getWeakLimitCone F).cone + +/-- An arbitrary choice of weak limit object of a functor. -/ +def weakLimit (F : J ⥤ C) [HasWeakLimit F] := + (weakLimit.cone F).pt + +/-- The projection from the weak limit object to a value of the functor. -/ +def weakLimit.π (F : J ⥤ C) [HasWeakLimit F] (j : J) : weakLimit F ⟶ F.obj j := + (weakLimit.cone F).π.app j + +@[reassoc] +theorem weakLimit.π_comp_eqToHom (F : J ⥤ C) [HasWeakLimit F] {j j' : J} (hj : j = j') : + weakLimit.π F j ≫ eqToHom (by subst hj; rfl) = weakLimit.π F j' := by + subst hj + simp + +@[simp] +theorem weakLimit.cone_pt {F : J ⥤ C} [HasWeakLimit F] : + (weakLimit.cone F).pt = weakLimit F := rfl + +@[simp] +theorem weakLimit.cone_π {F : J ⥤ C} [HasWeakLimit F] : + (weakLimit.cone F).π.app = weakLimit.π _ := rfl + +@[reassoc (attr := simp)] +theorem weakLimit.w (F : J ⥤ C) [HasWeakLimit F] {j j' : J} (f : j ⟶ j') : + weakLimit.π F j ≫ F.map f = weakLimit.π F j' := + (weakLimit.cone F).w f + +/-- Evidence that the arbitrary choice of cone provided by `weakLimit.cone F` +is a weak limit cone. -/ +def weakLimit.isWeakLimit (F : J ⥤ C) [HasWeakLimit F] : + IsWeakLimit (weakLimit.cone F) := + (getWeakLimitCone F).isWeakLimit + +/-- A morphism from the cone point of any other cone to the weak limit object. -/ +def weakLimit.lift (F : J ⥤ C) [HasWeakLimit F] (c : Cone F) : + c.pt ⟶ weakLimit F := + (weakLimit.isWeakLimit F).lift c + +@[simp] +theorem weakLimit.isWeakLimit_lift {F : J ⥤ C} [HasWeakLimit F] (c : Cone F) : + (weakLimit.isWeakLimit F).lift c = weakLimit.lift F c := + rfl + +@[reassoc (attr := simp)] +theorem weakLimit.lift_π {F : J ⥤ C} [HasWeakLimit F] (c : Cone F) (j : J) : + weakLimit.lift F c ≫ weakLimit.π F j = c.π.app j := + IsWeakLimit.fac _ c j + +namespace IsWeakLimit + +/-- Transport evidence that a cone is a weak limit cone across an isomorphism of cones. -/ +@[simps] +def ofIsoWeakLimit {r t : Cone F} (P : IsWeakLimit r) (i : r ≅ t) : IsWeakLimit t where + lift s := P.lift s ≫ i.hom.hom + +/-- Isomorphism of cones preserves whether or not they are weak limit cones. -/ +def equivIsoWeakLimit {r t : Cone F} (i : r ≅ t) : IsWeakLimit r ≃ IsWeakLimit t where + toFun h := h.ofIsoWeakLimit i + invFun h := h.ofIsoWeakLimit i.symm + left_inv _ := by simp [ofIsoWeakLimit] + right_inv _ := by simp [ofIsoWeakLimit] + +@[simp] +theorem equivIsoWeakLimit_apply {r t : Cone F} (i : r ≅ t) (P : IsWeakLimit r) : + equivIsoWeakLimit i P = P.ofIsoWeakLimit i := + rfl + +@[simp] +theorem equivIsoWeakLimit_symm_apply {r t : Cone F} (i : r ≅ t) (P : IsWeakLimit t) : + (equivIsoWeakLimit i).symm P = P.ofIsoWeakLimit i.symm := + rfl + +/-- The versal morphism from any other cone to a weak limit cone. -/ +@[simps] +def liftConeMorphism {t : Cone F} (h : IsWeakLimit t) (s : Cone F) : s ⟶ t where hom := h.lift s + +/-- Alternative constructor for `isWeakLimit`, +providing a morphism of cones rather than a morphism between the cone points +and separately the factorisation condition. +-/ +@[simps] +def mkOfConeMorphism {t : Cone F} (lift : ∀ s : Cone F, s ⟶ t) : IsWeakLimit t where + lift s := (lift s).hom + +/-- Given a right adjoint functor between categories of cones, +the image of a weak limit cone is a weak limit cone. +-/ +def ofRightAdjoint {left : Cone F ⥤ Cone G} {right : Cone G ⥤ Cone F} + (adj : left ⊣ right) {c : Cone G} (t : IsWeakLimit c) : IsWeakLimit (right.obj c) := + mkOfConeMorphism (fun s => adj.homEquiv s c (t.liftConeMorphism _)) + +/-- Given two functors which have equivalent categories of cones, we can transport evidence of +a weak limit cone across the equivalence. +-/ +lemma iff_of_cone_equiv {D : Type*} [Category* D] {G : K ⥤ D} (h : Cone G ≌ Cone F) {c : Cone G} : + Nonempty (IsWeakLimit (h.functor.obj c)) ↔ Nonempty (IsWeakLimit c) := + ⟨fun P ↦ Nonempty.intro (IsWeakLimit.ofIsoWeakLimit + (IsWeakLimit.ofRightAdjoint h.toAdjunction P.some) (h.unitIso.symm.app c)), + fun P ↦ Nonempty.intro (IsWeakLimit.ofRightAdjoint h.symm.toAdjunction P.some)⟩ + +/-- A cone postcomposed with a natural isomorphism is a weak limit cone +if and only if the original cone is. +-/ +lemma postcompose_hom_iff_of_iso {F G : J ⥤ C} (α : F ≅ G) (c : Cone F) : + Nonempty (IsWeakLimit ((Cone.postcompose α.hom).obj c)) ↔ Nonempty (IsWeakLimit c) := + iff_of_cone_equiv (Cone.postcomposeEquivalence α) + +/-- A cone postcomposed with the inverse of a natural isomorphism is a weak limit cone +if and only if the original cone is. +-/ +lemma postcompose_inv_iff_of_iso {F G : J ⥤ C} (α : F ≅ G) (c : Cone G) : + Nonempty (IsWeakLimit ((Cone.postcompose α.inv).obj c)) ↔ Nonempty (IsWeakLimit c) := + postcompose_hom_iff_of_iso α.symm c + +/-- Constructing an equivalence between `Nonempty (IsWeakLimit c)` and `Nonempty (IsWeakLimit d)` +from a natural isomorphism between the underlying functors, and then an isomorphism between `c` +transported along this and `d`. +-/ +lemma iff_of_natIso_of_iso {F G : J ⥤ C} (α : F ≅ G) (c : Cone F) (d : Cone G) + (w : (Cone.postcompose α.hom).obj c ≅ d) : + Nonempty (IsWeakLimit c) ↔ Nonempty (IsWeakLimit d) := + (postcompose_hom_iff_of_iso α _).symm.trans (IsWeakLimit.equivIsoWeakLimit w).nonempty_congr + +end IsWeakLimit + +/-- If a functor `F` has a weak limit, so does any naturally isomorphic functor. +-/ +theorem hasWeakLimit_of_iso {F G : J ⥤ C} [HasWeakLimit F] (α : F ≅ G) : HasWeakLimit G := + HasWeakLimit.mk + { cone := (Cone.postcompose α.hom).obj (weakLimit.cone F) + isWeakLimit := + Nonempty.some ((IsWeakLimit.postcompose_hom_iff_of_iso α _ ).mpr + (Nonempty.intro (weakLimit.isWeakLimit F))) } + +theorem hasWeakLimit_iff_of_iso {F G : J ⥤ C} (α : F ≅ G) : HasWeakLimit F ↔ HasWeakLimit G := + ⟨fun _ ↦ hasWeakLimit_of_iso α, fun _ ↦ hasWeakLimit_of_iso α.symm⟩ + +end CategoryTheory.Limits diff --git a/Mathlib/CategoryTheory/Limits/WeakLimits/WeakEqualizers.lean b/Mathlib/CategoryTheory/Limits/WeakLimits/WeakEqualizers.lean new file mode 100644 index 00000000000000..ce51e6a949d41c --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/WeakLimits/WeakEqualizers.lean @@ -0,0 +1,127 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Limits.WeakLimits.Basic +public import Mathlib.CategoryTheory.Limits.Shapes.Equalizers + +/-! +# Weak equalizers + +These are weak limits for diagrams of shape `WalkingParallelPair`. + +-/ + +@[expose] public section + +universe u v w + +noncomputable section + +open CategoryTheory Category Limits + +variable {C : Type*} [Category* C] + +namespace CategoryTheory.Limits + +variable {X Y : C} (f g : X ⟶ Y) + +/-- Two parallel morphisms `f` and `g` have a weak equalizer if the diagram `parallelPair f g` +has a weak limit. -/ +abbrev HasWeakEqualizer := + HasWeakLimit (parallelPair f g) + +variable [HasWeakEqualizer f g] + +/-- If a weak equalizer of `f` and `g` exists, we can access an arbitrary choice of such by +saying `weakEqualizer f g`. -/ +noncomputable abbrev weakEqualizer : C := + weakLimit (parallelPair f g) + +/-- If a weak equalizer of `f` and `g` exists, we can access the morphism +`weakEqualizer f g ⟶ X` by saying `weakEqualizer.ι f g`. -/ +noncomputable abbrev weakEqualizer.ι : weakEqualizer f g ⟶ X := + weakLimit.π (parallelPair f g) WalkingParallelPair.zero + +/-- A weak equalizer cone for a parallel pair `f` and `g` -/ +noncomputable abbrev weakEqualizer.fork : Fork f g := + weakLimit.cone (parallelPair f g) + +@[simp] +theorem weakEqualizer.fork_ι : (weakEqualizer.fork f g).ι = weakEqualizer.ι f g := + rfl + +@[simp] +theorem weakEqualizer.fork_π_app_zero : + (weakEqualizer.fork f g).π.app WalkingParallelPair.zero = weakEqualizer.ι f g := + rfl + +@[reassoc] +theorem weakEqualizer.condition : weakEqualizer.ι f g ≫ f = weakEqualizer.ι f g ≫ g := + Fork.condition <| weakLimit.cone <| parallelPair f g + +set_option backward.defeqAttrib.useBackward true in +/-- The weak equalizer built from `weakEqualizer.ι f g` is weakly limiting. -/ +def weakEqualizerIsWeakEqualizer : IsWeakLimit (Fork.ofι (weakEqualizer.ι f g) + (weakEqualizer.condition f g)) := + IsWeakLimit.ofIsoWeakLimit (weakLimit.isWeakLimit _) (Fork.ext (Iso.refl _) (by simp)) + +variable {f g} + +/-- A morphism `k : W ⟶ X` satisfying `k ≫ f = k ≫ g` factors through the weak equalizer of +`f` and `g` via `weakEqualizer.lift : W ⟶ weakEqualizer f g`. -/ +noncomputable abbrev weakEqualizer.lift {W : C} (k : W ⟶ X) (h : k ≫ f = k ≫ g) : + W ⟶ weakEqualizer f g := + weakLimit.lift (parallelPair f g) (Fork.ofι k h) + +@[reassoc] +theorem weakEqualizer.lift_ι {W : C} (k : W ⟶ X) (h : k ≫ f = k ≫ g) : + weakEqualizer.lift k h ≫ weakEqualizer.ι f g = k := + weakLimit.lift_π _ _ + +/-- A morphism `k : W ⟶ X` satisfying `k ≫ f = k ≫ g` induces a morphism +`l : W ⟶ weakEqualizer f g` satisfying `l ≫ weakEqualizer.ι f g = k`. -/ +def weakEqualizer.lift' {W : C} (k : W ⟶ X) (h : k ≫ f = k ≫ g) : + { l : W ⟶ weakEqualizer f g // l ≫ weakEqualizer.ι f g = k } := + ⟨weakEqualizer.lift k h, weakEqualizer.lift_ι _ _⟩ + +variable (C) + +/-- A category `HasWeakEqualizers` if it has all weak limits of shape `WalkingParallelPair`, +i.e. if it has a weak equalizer for every parallel pair of morphisms. -/ +abbrev HasWeakEqualizers := + HasWeakLimitsOfShape WalkingParallelPair C + +/-- A category with equalizers has weak equalizers. -/ +instance (priority := 100) HasWeakEqualizersOfHasEqualizers [HasEqualizers C] : + HasWeakEqualizers C where + +/-- If `C` has all weak limits of diagrams `parallelPair f g`, then it has all weak equalizers -/ +theorem hasWeakEqualizers_of_hasWeakLimit_parallelPair + [∀ {X Y : C} {f g : X ⟶ Y}, HasWeakLimit (parallelPair f g)] : HasWeakEqualizers C where + hasWeakLimit F := hasWeakLimit_of_iso (diagramIsoParallelPair F).symm + +variable {C} + +/-- This is a slightly more convenient method to verify that a fork is a weak limit cone. It +only asks for a proof of facts that carry any mathematical content -/ +@[simps] +def Fork.IsWeakLimit.mk (t : Fork f g) (lift : ∀ s : Fork f g, s.pt ⟶ t.pt) + (fac : ∀ s : Fork f g, lift s ≫ Fork.ι t = Fork.ι s) : IsWeakLimit t := + { lift + fac s j := + WalkingParallelPair.casesOn j (fac s) <| by + simp [← Category.assoc, fac] } + +/-- This is another convenient method to verify that a fork is a weak limit cone. It +only asks for a proof of facts that carry any mathematical content, and allows access to the +same `s` for all parts. -/ +def Fork.IsWeakLimit.mk' {X Y : C} {f g : X ⟶ Y} (t : Fork f g) + (create : ∀ s : Fork f g, { l // l ≫ t.ι = s.ι}) : + IsWeakLimit t := + Fork.IsWeakLimit.mk t (fun s => (create s).1) (fun s => (create s).2) + +end CategoryTheory.Limits diff --git a/Mathlib/CategoryTheory/Limits/WeakLimits/WeakKernels.lean b/Mathlib/CategoryTheory/Limits/WeakLimits/WeakKernels.lean new file mode 100644 index 00000000000000..d6a9baeaf14f22 --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/WeakLimits/WeakKernels.lean @@ -0,0 +1,138 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Limits.WeakLimits.WeakEqualizers +public import Mathlib.CategoryTheory.Limits.Shapes.Kernels +public import Mathlib.CategoryTheory.Preadditive.Basic + +/-! +# Weak kernels + +These are weak equalizers for functors of the form `parallelPair f 0`. + +If the category is preadditive, then weak equalizers exist if and only if weak kernels exist. +(See `hasWeakEqualizer_of_hasWeakKernel` and `hasWeakKernel_of_hasWeakEqualizer`.) + +-/ + +@[expose] public section + +universe u v w + +noncomputable section + +open CategoryTheory Category Limits + +variable {C : Type*} [Category* C] + +namespace CategoryTheory.Limits + +variable [HasZeroMorphisms C] {X Y : C} (f g : X ⟶ Y) + +/-- A morphism `f` has a weak kernel if the functor `ParallelPair f 0` has a weak limit. -/ +abbrev HasWeakKernel : Prop := + HasWeakLimit (parallelPair f 0) + +variable (C) in +/-- `HasWeakKernels` represents the existence of weak kernels for every morphism. -/ +class HasWeakKernels : Prop where + hasWeakLimit : ∀ {X Y : C} (f : X ⟶ Y), HasWeakKernel f := by infer_instance + +attribute [instance 100] HasWeakKernels.hasWeakLimit + +/-- If a category has kernels, then it has weak kernels. -/ +instance (priority := 100) HasWeakKernelsOfHasKernels [HasKernels C] : + HasWeakKernels C where + +section + +variable [HasWeakKernel f] + +/-- The weak kernel of a morphism. -/ +abbrev weakKernel : C := + weakEqualizer f 0 + +/-- The map from `weakKernel f` into the source of `f`. -/ +abbrev weakKernel.ι : weakKernel f ⟶ X := + weakEqualizer.ι f 0 + +@[simp] +theorem weakEqualizer_as_weakKernel : weakEqualizer.ι f 0 = weakKernel.ι f := rfl + +@[reassoc (attr := simp)] +theorem weakKernel.condition : weakKernel.ι f ≫ f = 0 := + KernelFork.condition _ + +set_option backward.defeqAttrib.useBackward true in +/-- The weak kernel built from `weakKernel.ι f` is weakly limiting. -/ +def weakKernelIsWeakKernel : + IsWeakLimit (Fork.ofι (weakKernel.ι f) ((weakKernel.condition f).trans comp_zero.symm)) := + IsWeakLimit.ofIsoWeakLimit (weakLimit.isWeakLimit _) (Fork.ext (Iso.refl _) (by simp)) + +/-- Given any morphism `k : W ⟶ X` satisfying `k ≫ f = 0`, `k` factors through +`weakKernel.ι f` via `weakKernel.lift : W ⟶ weakKernel f`. -/ +abbrev weakKernel.lift {W : C} (k : W ⟶ X) (h : k ≫ f = 0) : W ⟶ weakKernel f := + (weakKernelIsWeakKernel f).lift (KernelFork.ofι k h) + +@[reassoc (attr := simp)] +theorem weakKernel.lift_ι {W : C} (k : W ⟶ X) (h : k ≫ f = 0) : + weakKernel.lift f k h ≫ weakKernel.ι f = k := + (weakKernelIsWeakKernel f).fac (KernelFork.ofι k h) WalkingParallelPair.zero + +/-- Any morphism `k : W ⟶ X` satisfying `k ≫ f = 0` induces a morphism `l : W ⟶ weakKernel f` +such that `l ≫ weakKernel.ι f = k`. -/ +def weakKernel.lift' {W : C} (k : W ⟶ X) (h : k ≫ f = 0) : + { l : W ⟶ weakKernel f // l ≫ weakKernel.ι f = k } := + ⟨weakKernel.lift f k h, weakKernel.lift_ι _ _ _⟩ + +end + +end Limits + +namespace Preadditive + +variable [Preadditive C] {X Y : C} {f g : X ⟶ Y} + +/-- A weak kernel of `f - g` is a weak equalizer of `f` and `g`. -/ +def isWeakLimitForkOfKernelFork {c : KernelFork (f - g)} (i : IsWeakLimit c) : + IsWeakLimit (forkOfKernelFork c) := + Fork.IsWeakLimit.mk' _ fun s => ⟨i.lift (kernelForkOfFork s), i.fac _ _⟩ + +@[simp] +theorem isWeakLimitForkOfKernelFork_lift {c : KernelFork (f - g)} (i : IsWeakLimit c) + (s : Fork f g) : (isWeakLimitForkOfKernelFork i).lift s = i.lift (kernelForkOfFork s) := + rfl + +/-- A weak equalizer of `f` and `g` is a weak kernel of `f - g`. -/ +def isWeakLimitKernelForkOfFork {c : Fork f g} (i : IsWeakLimit c) : + IsWeakLimit (kernelForkOfFork c) := + Fork.IsWeakLimit.mk' _ fun s => ⟨i.lift (forkOfKernelFork s), i.fac _ _⟩ + +variable (f g) + +/-- A preadditive category has a weak equalizer for `f` and `g` if it has a weak +kernel for `f - g`. -/ +theorem hasWeakEqualizer_of_hasWeakKernel [HasWeakKernel (f - g)] : HasWeakEqualizer f g := + HasWeakLimit.mk + { cone := forkOfKernelFork _ + isWeakLimit := isWeakLimitForkOfKernelFork (weakEqualizerIsWeakEqualizer (f - g) 0) } + +/-- A preadditive category has a weak kernel for `f - g` if it has a weak equalizer +for `f` and `g`. -/ +theorem hasWeakKernel_of_hasWeakEqualizer [HasWeakEqualizer f g] : HasWeakKernel (f - g) := + HasWeakLimit.mk + { cone := kernelForkOfFork (weakEqualizer.fork f g) + isWeakLimit := isWeakLimitKernelForkOfFork (weakLimit.isWeakLimit (parallelPair f g)) } + +/-- If a preadditive category has all weak kernels, then it also has all weak equalizers. -/ +theorem hasWeakEqualizers_of_hasWeakKernels [HasWeakKernels C] : HasWeakEqualizers C := + have {X Y : C} (f g : X ⟶ Y) := hasWeakEqualizer_of_hasWeakKernel f g + hasWeakEqualizers_of_hasWeakLimit_parallelPair C + +end Preadditive + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Limits/WeakLimits/WeakPullbacks.lean b/Mathlib/CategoryTheory/Limits/WeakLimits/WeakPullbacks.lean new file mode 100644 index 00000000000000..259502b1308092 --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/WeakLimits/WeakPullbacks.lean @@ -0,0 +1,249 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Limits.WeakLimits.WeakEqualizers + +/-! +# Weak pullbacks + +These are weak limits for diagrams of shape `WalkingCospan`. + +If a category has binary products and weak equalizers, then it has weak pullbacks +(see `hasWeakPullbacks_of_hasBinaryProducts_of_hasWeakEqualizers`). + +-/ + +@[expose] public section + +universe u v w + +noncomputable section + +open CategoryTheory Category Limits + +variable {C : Type*} [Category* C] + +namespace CategoryTheory.Limits + +variable {W X Y Z : C} + +/-- Two morphisms `f : X ⟶ Z` and `g : Y ⟶ Z` have a weak pullback if the diagram +`cospan f g` has a weak limit. -/ +abbrev HasWeakPullback {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) := + HasWeakLimit (cospan f g) + +/-- `weakPullback f g` computes the weak pullback of a pair of morphisms +with the same target. -/ +abbrev weakPullback {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [HasWeakPullback f g] := + weakLimit (cospan f g) + +/-- The cone associated to the weak pullback of `f` and `g` -/ +abbrev weakPullback.cone {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) + [HasWeakPullback f g] : PullbackCone f g := + weakLimit.cone (cospan f g) + +/-- The first projection of the weak pullback of `f` and `g`. -/ +abbrev weakPullback.fst {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [HasWeakPullback f g] : + weakPullback f g ⟶ X := + weakLimit.π (cospan f g) WalkingCospan.left + +/-- The second projection of the weak pullback of `f` and `g`. -/ +abbrev weakPullback.snd {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [HasWeakPullback f g] : + weakPullback f g ⟶ Y := + weakLimit.π (cospan f g) WalkingCospan.right + +/-- A pair of morphisms `h : W ⟶ X` and `k : W ⟶ Y` satisfying `h ≫ f = k ≫ g` induces a morphism +`weakPullback.lift : W ⟶ weakPullback f g`. -/ +abbrev weakPullback.lift {W X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} [HasWeakPullback f g] (h : W ⟶ X) + (k : W ⟶ Y) (w : h ≫ f = k ≫ g := by cat_disch) : W ⟶ weakPullback f g := + weakLimit.lift _ (PullbackCone.mk h k w) + +set_option backward.isDefEq.respectTransparency false in +lemma weakPullback.exists_lift {W X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [HasWeakPullback f g] + (h : W ⟶ X) (k : W ⟶ Y) (w : h ≫ f = k ≫ g := by cat_disch) : + ∃ (l : W ⟶ weakPullback f g), + l ≫ weakPullback.fst f g = h ∧ l ≫ weakPullback.snd f g = k := + ⟨weakPullback.lift h k, by simp⟩ + +/-- The cone associated to a weak pullback is a weak limit cone. -/ +abbrev weakPullback.isWeakLimit {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [HasWeakPullback f g] : + IsWeakLimit (weakPullback.cone f g) := + weakLimit.isWeakLimit (cospan f g) + +@[simp] +theorem weakLimit.pullbackConeFst_cone_cospan {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) + [HasWeakLimit (cospan f g)] : + PullbackCone.fst (weakLimit.cone (cospan f g)) = weakPullback.fst f g := rfl + +@[simp] +theorem weakLimit.pullbackConeSnd_cone_cospan {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) + [HasWeakLimit (cospan f g)] : + PullbackCone.snd (weakLimit.cone (cospan f g)) = weakPullback.snd f g := rfl + +@[reassoc] +theorem weakPullback.lift_fst {W X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} + [HasWeakPullback f g] (h : W ⟶ X) (k : W ⟶ Y) (w : h ≫ f = k ≫ g) : + weakPullback.lift h k w ≫ weakPullback.fst f g = h := + weakLimit.lift_π _ _ + +@[reassoc] +theorem weakPullback.lift_snd {W X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} + [HasWeakPullback f g] (h : W ⟶ X) (k : W ⟶ Y) (w : h ≫ f = k ≫ g) : + weakPullback.lift h k w ≫ weakPullback.snd f g = k := + weakLimit.lift_π _ _ + +/-- A pair of morphisms `h : W ⟶ X` and `k : W ⟶ Y` satisfying `h ≫ f = k ≫ g` induces a morphism +`l : W ⟶ weakPullback f g` such that `l ≫ weakPullback.fst = h` and `l ≫ weakPullback.snd = k`. -/ +def weakPullback.lift' {W X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} [HasWeakPullback f g] + (h : W ⟶ X) (k : W ⟶ Y) (w : h ≫ f = k ≫ g) : + { l : W ⟶ weakPullback f g // + l ≫ weakPullback.fst f g = h ∧ l ≫ weakPullback.snd f g = k } := + ⟨weakPullback.lift h k w, weakPullback.lift_fst _ _ _, weakPullback.lift_snd _ _ _⟩ + +@[reassoc] +theorem weakPullback.condition {X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} [HasWeakPullback f g] : + weakPullback.fst f g ≫ f = weakPullback.snd f g ≫ g := + PullbackCone.condition _ + +/-- Given such a diagram, then there is a natural morphism from the weak pullback of +`W ⟶ S` and `X ⟶ S` to the weak pullback of `Y ⟶ T` and `Z ⟶ T`. + +``` +W ⟶ Y + ↘ ↘ + S ⟶ T + ↗ ↗ +X ⟶ Z +``` +-/ +abbrev weakPullback.map {W X Y Z S T : C} (f₁ : W ⟶ S) (f₂ : X ⟶ S) [HasWeakPullback f₁ f₂] + (g₁ : Y ⟶ T) (g₂ : Z ⟶ T) [HasWeakPullback g₁ g₂] (i₁ : W ⟶ Y) (i₂ : X ⟶ Z) (i₃ : S ⟶ T) + (eq₁ : f₁ ≫ i₃ = i₁ ≫ g₁) (eq₂ : f₂ ≫ i₃ = i₂ ≫ g₂) : + weakPullback f₁ f₂ ⟶ weakPullback g₁ g₂ := + weakPullback.lift (weakPullback.fst f₁ f₂ ≫ i₁) (weakPullback.snd f₁ f₂ ≫ i₂) + (by simp only [Category.assoc, ← eq₁, ← eq₂, weakPullback.condition_assoc]) + +/-- A morphism from the weak pullback of `W ⟶ S` and `X ⟶ S` to the weak pullback of +`Y ⟶ T` and `Z ⟶ T` given `S ⟶ T`. -/ +abbrev weakPullback.mapDesc {X Y S T : C} (f : X ⟶ S) (g : Y ⟶ S) (i : S ⟶ T) [HasWeakPullback f g] + [HasWeakPullback (f ≫ i) (g ≫ i)] : weakPullback f g ⟶ weakPullback (f ≫ i) (g ≫ i) := + weakPullback.map f g (f ≫ i) (g ≫ i) (𝟙 _) (𝟙 _) i (Category.id_comp _).symm + (Category.id_comp _).symm + +namespace PullbackCone + +variable {X Y Z : C} {f : X ⟶ Z} {g : Y ⟶ Z} + +/-- This is a slightly more convenient method to verify that a pullback cone is a weak limit cone. +It only asks for a proof of facts that carry any mathematical content -/ +def isWeakLimitAux (t : PullbackCone f g) (lift : ∀ s : PullbackCone f g, s.pt ⟶ t.pt) + (fac_left : ∀ s : PullbackCone f g, lift s ≫ t.fst = s.fst) + (fac_right : ∀ s : PullbackCone f g, lift s ≫ t.snd = s.snd) : IsWeakLimit t := + { lift + fac := fun s j => Option.casesOn j (by + rw [← s.w WalkingCospan.Hom.inl, ← t.w WalkingCospan.Hom.inl, ← Category.assoc] + congr + exact fac_left s) + fun j' => WalkingPair.casesOn j' (fac_left s) (fac_right s)} + +/-- This is another convenient method to verify that a pullback cone is a weak limit cone. It +only asks for a proof of facts that carry any mathematical content, and allows access to the +same `s` for all parts. -/ +def isWeakLimitAux' (t : PullbackCone f g) + (create : + ∀ s : PullbackCone f g, { l // l ≫ t.fst = s.fst ∧ l ≫ t.snd = s.snd}) : + Limits.IsWeakLimit t := + PullbackCone.isWeakLimitAux t (fun s => (create s).1) + (fun s => (create s).2.1) (fun s => (create s).2.2) + +/-- This is a more convenient formulation to show that a `PullbackCone` constructed using +`PullbackCone.mk` is a weak limit cone. +-/ +def IsWeakLimit.mk {W : C} {fst : W ⟶ X} {snd : W ⟶ Y} (eq : fst ≫ f = snd ≫ g) + (lift : ∀ s : PullbackCone f g, s.pt ⟶ W) + (fac_left : ∀ s : PullbackCone f g, lift s ≫ fst = s.fst) + (fac_right : ∀ s : PullbackCone f g, lift s ≫ snd = s.snd) : + IsWeakLimit (PullbackCone.mk fst snd eq) := + isWeakLimitAux _ lift fac_left fac_right + +/-- If `t` is a weak limit pullback cone over `f` and `g` and `h : W ⟶ X` and `k : W ⟶ Y` are such +that `h ≫ f = k ≫ g`, then we get `l : W ⟶ t.pt`, which satisfies `l ≫ fst t = h` +and `l ≫ snd t = k`, see `IsWeakLimit.lift_fst` and `IsWeakLimit.lift_snd`. -/ +def IsWeakLimit.lift {t : PullbackCone f g} (ht : IsWeakLimit t) {W : C} (h : W ⟶ X) (k : W ⟶ Y) + (w : h ≫ f = k ≫ g) : W ⟶ t.pt := + ht.lift <| PullbackCone.mk _ _ w + +@[reassoc (attr := simp)] +lemma IsWeakLimit.lift_fst {t : PullbackCone f g} (ht : IsWeakLimit t) {W : C} (h : W ⟶ X) + (k : W ⟶ Y) (w : h ≫ f = k ≫ g) : IsWeakLimit.lift ht h k w ≫ PullbackCone.fst t = h := + ht.fac _ _ + +@[reassoc (attr := simp)] +lemma IsWeakLimit.lift_snd {t : PullbackCone f g} (ht : IsWeakLimit t) {W : C} (h : W ⟶ X) + (k : W ⟶ Y) (w : h ≫ f = k ≫ g) : IsWeakLimit.lift ht h k w ≫ PullbackCone.snd t = k := + ht.fac _ _ + +/-- If `t` is a weak limit pullback cone over `f` and `g` and `h : W ⟶ X` and `k : W ⟶ Y` are such +that `h ≫ f = k ≫ g`, then we have `l : W ⟶ t.pt` satisfying `l ≫ fst t = h` and `l ≫ snd t = k`. +-/ +def IsWeakLimit.lift' {t : PullbackCone f g} (ht : IsWeakLimit t) {W : C} (h : W ⟶ X) (k : W ⟶ Y) + (w : h ≫ f = k ≫ g) : + { l : W ⟶ t.pt // l ≫ PullbackCone.fst t = h ∧ l ≫ PullbackCone.snd t = k } := + ⟨IsWeakLimit.lift ht h k w, by simp⟩ + +/-- The pullback cone reconstructed using `PullbackCone.mk` from a pullback cone that is a +weak limit, is also a weak limit. -/ +def mkSelfIsWeakLimit {t : PullbackCone f g} (ht : IsWeakLimit t) : + IsWeakLimit (PullbackCone.mk t.fst t.snd t.condition) := + IsWeakLimit.ofIsoWeakLimit ht (PullbackCone.eta t) + +end PullbackCone + +/-- The weak pullback cone built from the weak pullback projections is a weak pullback. -/ +def weakPullbackIsWeakPullback {X Y Z : C} (f : X ⟶ Z) (g : Y ⟶ Z) [HasWeakPullback f g] : + IsWeakLimit (PullbackCone.mk (weakPullback.fst f g) (weakPullback.snd f g) + weakPullback.condition) := + PullbackCone.mkSelfIsWeakLimit <| weakPullback.isWeakLimit f g + +variable (C) + +/-- A category `HasWeakPullbacks` if it has all weak limits of shape `WalkingCospan`, i.e. if it +has a weak pullback for every pair of morphisms with the same codomain. -/ +abbrev HasWeakPullbacks := + HasWeakLimitsOfShape WalkingCospan C + +instance (priority := 100) HasWeakPullbacksOfHasPullbacks [HasPullbacks C] : + HasWeakPullbacks C where + +variable (f : X ⟶ Z) (g : Y ⟶ Z) + +set_option backward.isDefEq.respectTransparency false in +/-- If the product `X ⨯ Y` and the weak equalizer of `π₁ ≫ f` and `π₂ ≫ g` exist, then the +weak pullback of `f` and `g` exists: it is given by composing the equalizer with the projections. -/ +theorem hasWeakLimit_cospan_of_hasLimit_pair_of_hasWeakLimit_parallelPair [HasLimit (pair X Y)] + [HasWeakLimit (parallelPair (prod.fst ≫ f) (prod.snd ≫ g))] : HasWeakLimit (cospan f g) := + HasWeakLimit.mk + { cone := + PullbackCone.mk (weakEqualizer.ι (prod.fst ≫ f) (prod.snd ≫ g) ≫ prod.fst) + (weakEqualizer.ι _ _ ≫ prod.snd) <| by + rw [Category.assoc, weakEqualizer.condition] + simp + isWeakLimit := + PullbackCone.IsWeakLimit.mk _ (fun s ↦ weakEqualizer.lift + (prod.lift (s.π.app .left) (s.π.app .right)) <| by + simp [limit.lift_π_assoc, PullbackCone.condition]) + (by simp) (by simp) } + +attribute [local instance] hasWeakLimit_cospan_of_hasLimit_pair_of_hasWeakLimit_parallelPair in +/-- If a category has all binary products and all weak equalizers, then it also has all +weak pullbacks. As usual, this is not an instance, since there may be a more direct way to +construct weak pullbacks. -/ +theorem hasWeakPullbacks_of_hasBinaryProducts_of_hasWeakEqualizers + [HasBinaryProducts C] [HasWeakEqualizers C] : HasWeakPullbacks C where + hasWeakLimit F := hasWeakLimit_of_iso (diagramIsoCospan F).symm + +end CategoryTheory.Limits diff --git a/Mathlib/CategoryTheory/Limits/Weighted/HasWeightedLimit.lean b/Mathlib/CategoryTheory/Limits/Weighted/HasWeightedLimit.lean new file mode 100644 index 00000000000000..d5a8ae9fbe39b0 --- /dev/null +++ b/Mathlib/CategoryTheory/Limits/Weighted/HasWeightedLimit.lean @@ -0,0 +1,254 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou, Yun Liu, Christian Merten, Robin Carlier +-/ +module + +public import Mathlib.CategoryTheory.Elements +public import Mathlib.CategoryTheory.Limits.HasLimits + +/-! +# Weighted limits + +In this file, we define weighted limits (in the non enriched case). +Given a weight `W : J ⥤ Type w` and a functor `F : J ⥤ C`, +the `W`-weighted limit of `J` is the limit of the functor +`CategoryOfElements.π W ⋙ F : W.Elements ⥤ C`. + +## References +* https://ncatlab.org/nlab/show/weighted+limit + +-/ + +@[expose] public section + +universe w v u v' u' + +namespace CategoryTheory + +open Limits Opposite + +namespace Limits + +variable {J : Type u} [Category.{v} J] {C : Type u'} [Category.{v'} C] + +/-- Given `W : J ⥤ Type w` and `F : J ⥤ C`, this is the type of cones for +the functor `CategoryOfElements.π W ⋙ F : W.Elements ⥤ C`. -/ +abbrev WeightedCone (W : J ⥤ Type w) (F : J ⥤ C) := + Cone (CategoryOfElements.π W ⋙ F) + +/-- Given a weight `W : J ⥤ Type w` and `F : J ⥤ C`, we say that +the `W`-weighted limit of `F` exists if the functor +`CategoryOfElements.π W ⋙ F : W.Elements ⥤ C` has a limit. -/ +abbrev HasWeightedLimit (W : J ⥤ Type w) (F : J ⥤ C) : Prop := + HasLimit (CategoryOfElements.π W ⋙ F) + +namespace WeightedCone + +variable {W : J ⥤ Type w} {F : J ⥤ C} + +/-- The projection `c.pt ⟶ F.obj j` for `c : WeightedCone W F` +and `x : W.obj j`. -/ +protected abbrev π (c : WeightedCone W F) {j : J} (x : W.obj j) : + c.pt ⟶ F.obj j := + (Cone.π c).app (Functor.elementsMk _ _ x) + +@[reassoc (attr := simp)] +protected lemma w (c : WeightedCone W F) {i j : J} (x : W.obj i) (f : i ⟶ j) : + c.π x ≫ F.map f = c.π (W.map f x) := + Cone.w c (CategoryOfElements.homMk (Functor.elementsMk _ _ x) + (Functor.elementsMk _ _ (W.map f x)) f rfl) + +variable (pt : C) (π : ∀ ⦃j : J⦄ (_ : W.obj j), pt ⟶ F.obj j) + (hπ : ∀ ⦃j₁ j₂ : J⦄ (x : W.obj j₁) (f : j₁ ⟶ j₂), + π x ≫ F.map f = π (W.map f x)) + +set_option backward.defeqAttrib.useBackward true in +/-- Constructor for weighted cones. -/ +@[simps pt] +def mk : WeightedCone W F where + pt := pt + π.app x := π x.snd + π.naturality x₁ x₂ f := by simpa using (hπ x₁.snd f.val).symm + +@[simp] +lemma mk_π {j : J} (x : W.obj j) : + (mk pt π hπ).π x = π x := rfl + +/-- A weighted cone `c : WeightedCone W F` is a limit if it is so +as a cone of `CategoryOfElements.π W ⋙ F : W.Elements ⥤ C`. -/ +protected abbrev IsLimit (c : WeightedCone W F) := Limits.IsLimit c + +namespace IsLimit + +variable {c : WeightedCone W F} (hc : c.IsLimit) {Z : C} + +include hc in +lemma hasWeightedLimit : HasWeightedLimit W F := ⟨_, hc⟩ + +section + +variable + (π : ∀ ⦃j : J⦄ (_ : W.obj j), Z ⟶ F.obj j) + (hπ : ∀ ⦃j₁ j₂ : J⦄ (x : W.obj j₁) (f : j₁ ⟶ j₂), + π x ≫ F.map f = π (W.map f x)) + +/-- Constructor for morphisms from the point of a limit weighted cone. -/ +def lift : Z ⟶ c.pt := + Limits.IsLimit.lift hc (WeightedCone.mk Z π hπ) + +@[reassoc (attr := simp)] +lemma fac {j : J} (x : W.obj j) : + hc.lift π hπ ≫ c.π x = π x := + Limits.IsLimit.fac hc (WeightedCone.mk Z π hπ) (Functor.elementsMk _ _ x) + +end + +include hc in +lemma hom_ext {f g : Z ⟶ c.pt} (h : ∀ {j : J} (x : W.obj j), f ≫ c.π x = g ≫ c.π x) : + f = g := + Limits.IsLimit.hom_ext hc (fun _ ↦ h _) + +end IsLimit + +open Opposite in +set_option backward.defeqAttrib.useBackward true in +/-- If the weight is `coyoneda.obj (op j) : J ⥤ Type _`, this is the limit +weighted cone for `F : J ⥤ C` with point `F.obj j`. -/ +@[simps] +protected abbrev coyoneda (F : J ⥤ C) (j : J) : + WeightedCone (coyoneda.obj (op j)) F where + pt := F.obj j + π.app u := F.map u.snd + π.naturality _ _ f := by simp [← Functor.map_comp, Category.id_comp, f.prop.symm] + +set_option backward.defeqAttrib.useBackward true in +/-- The weighted limit of `F` for the weight `coyoneda.obj (op j)` is `F.obj j`. -/ +def isLimitCoyoneda (F : J ⥤ C) (j : J) : (WeightedCone.coyoneda F j).IsLimit where + lift s := WeightedCone.π s (𝟙 j) + fac s x := by + simpa using s.w (CategoryOfElements.homMk (Functor.elementsMk _ j (𝟙 j)) x x.snd (by simp)) + uniq s m hm := by + simpa using hm (Functor.elementsMk _ j (𝟙 j)) + +end WeightedCone + +end Limits + +namespace Functor + +section + +variable {J : Type u} [Category.{v} J] {C : Type u'} [Category.{v'} C] + (W W' W'' : J ⥤ Type w) (g : W ⟶ W') (g' : W' ⟶ W'') (F : J ⥤ C) + [HasWeightedLimit W F] [HasWeightedLimit W' F] [HasWeightedLimit W'' F] + +/-- Given a weight `W : J ⥤ Type w` and `F : J ⥤ C`, this is the `W`-weighted +limit of `F`. -/ +noncomputable def weightedLimObjObj : C := + limit (CategoryOfElements.π W ⋙ F) + +/-- The projections from the weighted limit. -/ +@[no_expose] +noncomputable def weightedLimObjObjπ ⦃j : J⦄ (x : W.obj j) : + W.weightedLimObjObj F ⟶ F.obj j := + limit.π (CategoryOfElements.π W ⋙ F) (Functor.elementsMk _ _ x) + +@[reassoc (attr := simp)] +lemma weightedLimObjObj_w ⦃j₁ j₂ : J⦄ (x : W.obj j₁) + (f : j₁ ⟶ j₂) : + W.weightedLimObjObjπ F x ≫ F.map f = + W.weightedLimObjObjπ F (W.map f x) := + limit.w (CategoryOfElements.π W ⋙ F) + (CategoryOfElements.homMk (Functor.elementsMk _ _ x) (Functor.elementsMk _ _ + (W.map f x)) f rfl) + +/-- A choice of limit weighted cone. -/ +noncomputable abbrev weightedLimCone : + WeightedCone W F := + WeightedCone.mk (W.weightedLimObjObj F) + (fun j x ↦ W.weightedLimObjObjπ F x) + (fun j₁ j₂ x f ↦ by simp) + +/-- The weighted cone `W.weightedLimCone F` is a limit. -/ +@[no_expose] +noncomputable def isLimitWeightedLimCone : + (W.weightedLimCone F).IsLimit := + limit.isLimit _ + +@[reassoc (attr := simp)] +lemma isLimitWeightedLimCone_fac {Z} (π) (hπ) ⦃j : J⦄ (x : W.obj j) : + (W.isLimitWeightedLimCone F).lift (Z := Z) π hπ ≫ W.weightedLimObjObjπ F x = π x := + (W.isLimitWeightedLimCone F).fac .. + +variable {W F} in +@[ext] +lemma weightedLimObjObj.hom_ext {Z : C} {f g : Z ⟶ W.weightedLimObjObj F} + (h : ∀ {j : J} (x : W.obj j), + f ≫ W.weightedLimObjObjπ F x = g ≫ W.weightedLimObjObjπ F x) : + f = g := + (W.isLimitWeightedLimCone F).hom_ext h + +/-- Functoriality of the weighted limits with fixed weight `W : J ⥤ Type w` +with respect to the functor in `J ⥤ C`. -/ +@[no_expose] +noncomputable def weightedLimObjMap {F₁ F₂ : J ⥤ C} + [HasWeightedLimit W F₁] [HasWeightedLimit W F₂] (f : F₁ ⟶ F₂) : + W.weightedLimObjObj F₁ ⟶ W.weightedLimObjObj F₂ := + limMap (whiskerLeft _ f) + +@[reassoc (attr := simp)] +lemma weightedLimObjMap_π {F₁ F₂ : J ⥤ C} + [HasWeightedLimit W F₁] [HasWeightedLimit W F₂] (f : F₁ ⟶ F₂) + ⦃j : J⦄ (x : W.obj j) : + W.weightedLimObjMap f ≫ W.weightedLimObjObjπ F₂ x = + W.weightedLimObjObjπ F₁ x ≫ f.app j := + limit.lift_π .. + +@[simp] +lemma weightedLimObjMap_id (F : J ⥤ C) [HasWeightedLimit W F] : + W.weightedLimObjMap (𝟙 F) = 𝟙 _ := by + cat_disch + +@[reassoc] +lemma weightedLimObjMap_comp {F₁ F₂ F₃ : J ⥤ C} + [HasWeightedLimit W F₁] [HasWeightedLimit W F₂] [HasWeightedLimit W F₃] + (f : F₁ ⟶ F₂) (g : F₂ ⟶ F₃) : + W.weightedLimObjMap (f ≫ g) = W.weightedLimObjMap f ≫ W.weightedLimObjMap g := by + cat_disch + +section + +variable {W W' W''} + +/-- The (contravariant) functoriality of weighted limits with respect to the weight. -/ +noncomputable def weightedLimFlipObjMap : + W'.weightedLimObjObj F ⟶ W.weightedLimObjObj F := + (W.isLimitWeightedLimCone F).lift + (fun j x ↦ W'.weightedLimObjObjπ F (g.app j x)) (by simp) + +@[reassoc (attr := simp)] +lemma weightedLimObjObjMap_π ⦃j : J⦄ (x : W.obj j) : + weightedLimFlipObjMap g F ≫ W.weightedLimObjObjπ F x = + W'.weightedLimObjObjπ F (g.app j x) := + (W.isLimitWeightedLimCone F).fac .. + +@[simp] +lemma weightedLimFlipObjMap_id : + weightedLimFlipObjMap (𝟙 W) F = 𝟙 _ := by + cat_disch + +@[reassoc] +lemma weightedLimFlipObjMap_comp : + weightedLimFlipObjMap g' F ≫ weightedLimFlipObjMap g F = + weightedLimFlipObjMap (g ≫ g') F := by + cat_disch + +end + +end + +end Functor + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean b/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean index 1944773a64883d..a9539a1d84d6dc 100644 --- a/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean +++ b/Mathlib/CategoryTheory/Localization/DerivabilityStructure/Derives.lean @@ -65,7 +65,7 @@ section variable {L₂ : C₂ ⥤ D₂} [L₂.IsLocalization W₂] {RF : D₂ ⥤ H} (α : F ⟶ L₂ ⋙ RF) -lemma isIso (X₁ : C₁) [RF.IsRightDerivedFunctor α W₂] : +lemma isIso_of_isRightDerivedFunctor (X₁ : C₁) [RF.IsRightDerivedFunctor α W₂] : IsIso (α.app (Φ.functor.obj X₁)) := by let G : W₁.Localization ⥤ H := Localization.lift (Φ.functor ⋙ F) h W₁.Q let eG := Localization.Lifting.iso W₁.Q W₁ (Φ.functor ⋙ F) G @@ -74,12 +74,14 @@ lemma isIso (X₁ : C₁) [RF.IsRightDerivedFunctor α W₂] : rw [← Φ.isIso_iff_of_isRightDerivabilityStructure W₁.Q L₂ F G eG.inv RF α] infer_instance +@[deprecated (since := "2026-06-22")] alias isIso := isIso_of_isRightDerivedFunctor + set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in lemma isRightDerivedFunctor_of_isIso (hα : ∀ (X₁ : C₁), IsIso (α.app (Φ.functor.obj X₁))) : RF.IsRightDerivedFunctor α W₂ := by have := h.hasPointwiseRightDerivedFunctor - have := h.isIso (F.totalRightDerivedUnit L₂ W₂) + have := h.isIso_of_isRightDerivedFunctor (F.totalRightDerivedUnit L₂ W₂) have := Φ.essSurj_of_hasRightResolutions L₂ let φ := (F.totalRightDerived L₂ W₂).rightDerivedDesc (F.totalRightDerivedUnit L₂ W₂) W₂ RF α have hφ : F.totalRightDerivedUnit L₂ W₂ ≫ Functor.whiskerLeft L₂ φ = α := @@ -97,7 +99,7 @@ lemma isRightDerivedFunctor_of_isIso (hα : ∀ (X₁ : C₁), IsIso (α.app (Φ lemma isRightDerivedFunctor_iff_isIso : RF.IsRightDerivedFunctor α W₂ ↔ ∀ (X₁ : C₁), IsIso (α.app (Φ.functor.obj X₁)) := - ⟨fun _ _ ↦ h.isIso α _, h.isRightDerivedFunctor_of_isIso α⟩ + ⟨fun _ _ ↦ h.isIso_of_isRightDerivedFunctor α _, h.isRightDerivedFunctor_of_isIso α⟩ end diff --git a/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean b/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean index d69534f721a440..2be304d60b9ec4 100644 --- a/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean +++ b/Mathlib/CategoryTheory/Localization/LocalizerMorphism.lean @@ -64,6 +64,9 @@ def id : LocalizerMorphism W₁ W₁ where functor := 𝟭 C₁ map _ _ _ hf := hf +instance : (id W₁).functor.IsEquivalence := + inferInstanceAs (𝟭 C₁).IsEquivalence + variable {W₁ W₂ W₃} /-- The composition of two localizers morphisms. -/ diff --git a/Mathlib/CategoryTheory/Localization/Predicate.lean b/Mathlib/CategoryTheory/Localization/Predicate.lean index 28242d1d57c21d..c882cb52d75bcb 100644 --- a/Mathlib/CategoryTheory/Localization/Predicate.lean +++ b/Mathlib/CategoryTheory/Localization/Predicate.lean @@ -127,6 +127,9 @@ theorem IsLocalization.for_id (hW : W ≤ MorphismProperty.isomorphisms C) : ( IsLocalization.mk' _ _ (Localization.strictUniversalPropertyFixedTargetId W _ hW) (Localization.strictUniversalPropertyFixedTargetId W _ hW) +instance : (𝟭 C).IsLocalization (MorphismProperty.isomorphisms C) := + IsLocalization.for_id _ (by rfl) + end Functor namespace Localization diff --git a/Mathlib/CategoryTheory/LocallyCartesianClosed/Over.lean b/Mathlib/CategoryTheory/LocallyCartesianClosed/Over.lean index 7f3cc8cf2e0ec2..6f3bd6085f89cf 100644 --- a/Mathlib/CategoryTheory/LocallyCartesianClosed/Over.lean +++ b/Mathlib/CategoryTheory/LocallyCartesianClosed/Over.lean @@ -302,9 +302,9 @@ def forgetAdjToOver (X : C) : Over.forget X ⊣ toOver X where counit.app Z := fst Z X theorem forgetAdjToOver.homEquiv_symm {X : C} (Z : Over X) (A : C) (f : Z ⟶ (toOver X).obj A) : - ((forgetAdjToOver X).homEquiv Z A).symm f = f.left ≫ (fst _ _) := by - rw [Adjunction.homEquiv_counit, forgetAdjToOver_counit_app] - simp + ((forgetAdjToOver X).homEquiv Z A).symm f = f.left ≫ (fst _ _) := by + rw [Adjunction.homEquiv_counit, forgetAdjToOver_counit_app] + simp /-- The isomorphism of functors `toOver (𝟙_ C)` and `toOverUnit C`. -/ @[simps!] diff --git a/Mathlib/CategoryTheory/Monad/Limits.lean b/Mathlib/CategoryTheory/Monad/Limits.lean index cb06848c5cea2d..e74d2fc24dc126 100644 --- a/Mathlib/CategoryTheory/Monad/Limits.lean +++ b/Mathlib/CategoryTheory/Monad/Limits.lean @@ -413,11 +413,11 @@ def coconePoint : Coalgebra T where A := c.pt a := t.desc (newCocone D c) counit := t.hom_ext fun j ↦ by - simp only [Functor.comp_obj, forget_obj, Functor.id_obj, Functor.const_obj_obj, + simp only [Functor.comp_obj, forget_obj, Functor.id_obj, IsColimit.fac_assoc, newCocone_ι_app, assoc, NatTrans.naturality, Functor.id_map, comp_id] rw [← Category.assoc, (D.obj j).counit, Category.id_comp] coassoc := t.hom_ext fun j ↦ by - simp only [Functor.comp_obj, forget_obj, Functor.const_obj_obj, IsColimit.fac_assoc, + simp only [Functor.comp_obj, forget_obj, IsColimit.fac_assoc, newCocone_ι_app, assoc, NatTrans.naturality, Functor.comp_map] rw [← Category.assoc, (D.obj j).coassoc, ← Functor.map_comp, t.fac (newCocone D c) j, newCocone_ι_app, Functor.map_comp, assoc] diff --git a/Mathlib/CategoryTheory/Monoidal/Bimon_.lean b/Mathlib/CategoryTheory/Monoidal/Bimon_.lean index e24b705f6e8de5..299305871404f6 100644 --- a/Mathlib/CategoryTheory/Monoidal/Bimon_.lean +++ b/Mathlib/CategoryTheory/Monoidal/Bimon_.lean @@ -172,7 +172,7 @@ set_option backward.isDefEq.respectTransparency false in @[simps!] def equivMonComonUnitIsoAppX (M : Bimon C) : M.X ≅ ((toMonComon C ⋙ ofMonComon C).obj M).X := - Mon.mkIso (equivMonComonUnitIsoAppXAux M) + Mon.mkIso (equivMonComonUnitIsoAppXAux M) set_option backward.isDefEq.respectTransparency false in instance (M : Bimon C) : IsComonHom (equivMonComonUnitIsoAppX M).hom where @@ -216,7 +216,7 @@ set_option backward.isDefEq.respectTransparency false in @[simps!] def equivMonComonCounitIsoApp (M : Mon (Comon C)) : (ofMonComon C ⋙ toMonComon C).obj M ≅ M := - Mon.mkIso <| (equivMonComonCounitIsoAppX M) + Mon.mkIso <| (equivMonComonCounitIsoAppX M) /-- The equivalence `Comon (Mon C) ≌ Mon (Comon C)` -/ def equivMonComon : Bimon C ≌ Mon (Comon C) where diff --git a/Mathlib/CategoryTheory/Monoidal/Braided/PushoutObjObj.lean b/Mathlib/CategoryTheory/Monoidal/Braided/PushoutObjObj.lean index e68cd152cf26cc..ec7d6b0b6cdef1 100644 --- a/Mathlib/CategoryTheory/Monoidal/Braided/PushoutObjObj.lean +++ b/Mathlib/CategoryTheory/Monoidal/Braided/PushoutObjObj.lean @@ -36,9 +36,4 @@ obtain a similar structure for `f₂` and `f₁`. -/ def flipTensor : (curriedTensor C).PushoutObjObj f₂ f₁ := sq.flip.ofNatIso (BraidedCategory.curriedBraidingNatIso _).symm -set_option backward.defeqAttrib.useBackward true in -@[simp] -lemma flipTensor_ι : dsimp% sq.flipTensor.ι = sq.ι ≫ (β_ _ _).inv := by - simp [flipTensor] - end CategoryTheory.Functor.PushoutObjObj diff --git a/Mathlib/CategoryTheory/Monoidal/DayConvolution/DayFunctor.lean b/Mathlib/CategoryTheory/Monoidal/DayConvolution/DayFunctor.lean index 248f5c4c941e9e..d921d186640088 100644 --- a/Mathlib/CategoryTheory/Monoidal/DayConvolution/DayFunctor.lean +++ b/Mathlib/CategoryTheory/Monoidal/DayConvolution/DayFunctor.lean @@ -158,13 +158,13 @@ def tensorDesc {F G H : C ⊛⥤ V} lemma η_comp_tensorDec {F G H : C ⊛⥤ V} (α : F.functor ⊠ G.functor ⟶ tensor C ⋙ H.functor) : - η F G ≫ Functor.whiskerLeft _ (tensorDesc α).natTrans = α := + η F G ≫ Functor.whiskerLeft _ (tensorDesc α).natTrans = α := Functor.descOfIsLeftKanExtension_fac _ _ _ _ @[reassoc (attr := simp)] lemma η_comp_tensorDesc_app {F G H : C ⊛⥤ V} (α : F.functor ⊠ G.functor ⟶ tensor C ⋙ H.functor) (x y : C) : - (η F G).app (x, y) ≫ (tensorDesc α).natTrans.app (x ⊗ y) = α.app (x, y) := + (η F G).app (x, y) ≫ (tensorDesc α).natTrans.app (x ⊗ y) = α.app (x, y) := Functor.descOfIsLeftKanExtension_fac_app _ _ _ _ _ open LawfulDayConvolutionMonoidalCategoryStruct diff --git a/Mathlib/CategoryTheory/Monoidal/Grp.lean b/Mathlib/CategoryTheory/Monoidal/Grp.lean index b532109706244c..c515ce6f7c0a53 100644 --- a/Mathlib/CategoryTheory/Monoidal/Grp.lean +++ b/Mathlib/CategoryTheory/Monoidal/Grp.lean @@ -57,7 +57,6 @@ namespace GrpObj attribute [reassoc (attr := simp)] left_inv right_inv attribute [reassoc (attr := simp)] AddGrpObj.left_neg AddGrpObj.right_neg -set_option linter.existingAttributeWarning false in attribute [to_additive existing] left_inv left_inv_assoc right_inv right_inv_assoc @[to_additive] diff --git a/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean b/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean index e3b93682c62958..db187334c5de14 100644 --- a/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean +++ b/Mathlib/CategoryTheory/Monoidal/OfHasFiniteProducts.lean @@ -181,6 +181,7 @@ variable [PreservesLimit (Functor.empty.{0} C) F] [PreservesLimitsOfShape (Discrete WalkingPair) F] set_option backward.defeqAttrib.useBackward true in +set_option linter.deprecated false in @[deprecated inferInstance (since := "2025-10-19")] instance : have : HasFiniteProducts C := hasFiniteProducts_of_has_binary_and_terminal @@ -190,6 +191,7 @@ instance : IsIso (η F) := by dsimp [η_eq]; apply instIsIsoTerminalComparison set_option backward.defeqAttrib.useBackward true in +set_option linter.deprecated false in @[deprecated inferInstance (since := "2025-10-19")] instance (X Y : C) : have : HasFiniteProducts C := hasFiniteProducts_of_has_binary_and_terminal diff --git a/Mathlib/CategoryTheory/Monoidal/PushoutProduct.lean b/Mathlib/CategoryTheory/Monoidal/PushoutProduct.lean index 15d96eeccc39c1..6ace1b822076ef 100644 --- a/Mathlib/CategoryTheory/Monoidal/PushoutProduct.lean +++ b/Mathlib/CategoryTheory/Monoidal/PushoutProduct.lean @@ -119,8 +119,8 @@ def whiskerLeftIso (associator_inv_naturality_middle W _ _).symm (associator_inv_naturality_right W _ _).symm)) (α_ W _ _).symm (((tensorLeft W).map_isPushout - (IsPushout.of_hasPushout (X₁.hom ▷ X₂.left) (X₁.left ◁ X₂.hom))).hom_ext (by - simp) (by simp)) + (IsPushout.of_hasPushout (X₁.hom ▷ X₂.left) (X₁.left ◁ X₂.hom))).hom_ext + (by simp [← whiskerLeft_comp_assoc]) (by simp [← whiskerLeft_comp_assoc])) set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in @@ -138,7 +138,8 @@ def whiskerRightIso (associator_naturality_left _ _ W).symm (associator_naturality_middle _ _ W).symm)) (α_ _ _ W) (((tensorRight W).map_isPushout - (IsPushout.of_hasPushout (X₁.hom ▷ X₂.left) (X₁.left ◁ X₂.hom))).hom_ext (by simp) (by simp)) + (IsPushout.of_hasPushout (X₁.hom ▷ X₂.left) (X₁.left ◁ X₂.hom))).hom_ext + (by simp [← comp_whiskerRight_assoc]) (by simp [← comp_whiskerRight_assoc])) -- helper instance for `PushoutProduct.associator` local instance {F : C ⥤ C} @@ -165,16 +166,19 @@ def associator pushout.desc (_ ◁ pushout.inr _ _ ≫ pushout.inl _ _) (pushout.inr _ _) (by simp [Limits.pushout.associator_naturality_left_condition])) (((tensorRight _).map_isPushout (IsPushout.of_hasPushout _ _)).hom_ext - (by simp [Limits.pushout.whiskerLeft_condition_assoc, ← whisker_exchange_assoc]) - (by simp [← whisker_exchange_assoc, Limits.pushout.associator_naturality_left_condition])) + (by simp [Limits.pushout.whiskerLeft_condition_assoc, ← whisker_exchange_assoc, + ← comp_whiskerRight_assoc]) + (by simp [← whisker_exchange_assoc, Limits.pushout.associator_naturality_left_condition, + ← comp_whiskerRight_assoc])) · exact pushout.desc ((whiskerLeftIso _ _).hom.left ≫ pushout.desc (pushout.inl _ _) ((pushout.inl _ _ ▷ _) ≫ pushout.inr _ _) (by simp [Limits.pushout.associator_inv_naturality_right_condition])) ((α_ _ _ _).inv ≫ (pushout.inr _ _) ▷ _ ≫ pushout.inr _ _) (((tensorLeft _).map_isPushout (IsPushout.of_hasPushout _ _)).hom_ext (by simp [whisker_exchange_assoc, - Limits.pushout.associator_inv_naturality_right_condition]) - (by simp [whisker_exchange_assoc, Limits.pushout.condition_whiskerRight_assoc])) + Limits.pushout.associator_inv_naturality_right_condition, ← whiskerLeft_comp_assoc]) + (by simp [whisker_exchange_assoc, Limits.pushout.condition_whiskerRight_assoc, + ← whiskerLeft_comp_assoc])) · apply pushout.hom_ext (by simp) apply ((tensorRight _).map_isPushout (IsPushout.of_hasPushout _ _)).hom_ext <;> simp · refine pushout.hom_ext ?_ (by simp) @@ -214,7 +218,7 @@ def isInitialIso (X : Arrow C) {I : C} (i : IsInitial I) {W : C} : haveI : IsPushout (X.hom ▷ I) (_ ◁ i.to W) ((i.ofIso (zeroMul i).symm).to _) (𝟙 _) := .of_horiz_isIso (sq := ⟨(i.ofIso (zeroMul i).symm).hom_ext ..⟩) Arrow.isoMk' _ _ this.isoPushout.symm (Iso.refl _) - (pushout.hom_ext ((i.ofIso (zeroMul i).symm).hom_ext _ _) (by simp)) + (pushout.hom_ext ((i.ofIso (zeroMul i).symm).hom_ext ..) (by simp [pushout.inr_desc])) set_option backward.defeqAttrib.useBackward true in /-- The arrow isomorphism `(∅ ⟶ W) □ X ≅ W ◁ X` in a braided CCC with pushouts and @@ -227,7 +231,7 @@ def isInitialIso' [BraidedCategory C] (X : Arrow C) {I : C} (i : IsInitial I) {W haveI : IsPushout (i.to W ▷ _) (I ◁ X.hom) (𝟙 _) ((i.ofIso (mulZero i).symm).to _) := .of_vert_isIso (sq := ⟨(i.ofIso (mulZero i).symm).hom_ext ..⟩) Arrow.isoMk' _ _ this.isoPushout.symm (Iso.refl _) - (pushout.hom_ext (by simp) ((i.ofIso (mulZero i).symm).hom_ext _ _)) + (pushout.hom_ext (by simp [pushout.inl_desc]) ((i.ofIso (mulZero i).symm).hom_ext _ _)) /-- The arrow isomorphism `X □ (∅ ⟶ ⋆) ≅ X` in a CCC with pushouts, an initial object, and a terminal object. -/ diff --git a/Mathlib/CategoryTheory/MorphismProperty/Comma.lean b/Mathlib/CategoryTheory/MorphismProperty/Comma.lean index 8a0d834d4922e1..b942cab0ba82c2 100644 --- a/Mathlib/CategoryTheory/MorphismProperty/Comma.lean +++ b/Mathlib/CategoryTheory/MorphismProperty/Comma.lean @@ -168,7 +168,7 @@ structure Hom (X Y : P.Comma L R Q W) extends CommaMorphism X.toComma Y.toComma abbrev Hom.hom {X Y : P.Comma L R Q W} (f : Comma.Hom X Y) : X.toComma ⟶ Y.toComma := f.toCommaMorphism -@[simp, nolint simpVarHead] +@[simp] lemma Hom.hom_mk {X Y : P.Comma L R Q W} (f : CommaMorphism X.toComma Y.toComma) (hf) (hg) : Comma.Hom.hom ⟨f, hf, hg⟩ = f := rfl diff --git a/Mathlib/CategoryTheory/MorphismProperty/Composition.lean b/Mathlib/CategoryTheory/MorphismProperty/Composition.lean index 37c150c2e5a9e8..69a532ca7dd9b7 100644 --- a/Mathlib/CategoryTheory/MorphismProperty/Composition.lean +++ b/Mathlib/CategoryTheory/MorphismProperty/Composition.lean @@ -1,7 +1,7 @@ /- Copyright (c) 2022 Andrew Yang. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Andrew Yang, Joël Riou +Authors: Andrew Yang, Joël Riou, Aras Ergus -/ module @@ -27,6 +27,18 @@ namespace MorphismProperty variable {C : Type u} [Category.{v} C] {D : Type u'} [Category.{v'} D] +variable (C) in +/-- The property of morphisms that is satisfied by `𝟙 X` for any `X`. -/ +abbrev identities : MorphismProperty C := + .ofHoms fun X ↦ 𝟙 X + +lemma identities_op_iff {X Y : Cᵒᵖ} (f : X ⟶ Y) : + identities Cᵒᵖ f ↔ identities C f.unop := by + obtain ⟨X⟩ := X + obtain ⟨f⟩ := f + dsimp + exact ⟨fun ⟨_⟩ ↦ ⟨_⟩, fun ⟨_⟩ ↦ ⟨_⟩⟩ + /-- Typeclass expressing that a morphism property contains identities. -/ class ContainsIdentities (W : MorphismProperty C) : Prop where /-- for all `X : C`, the identity of `X` satisfies the morphism property -/ @@ -72,6 +84,13 @@ instance iInf {ι : Type*} {W : ι → MorphismProperty C} rw [← sInf_range] exact sInf (by simpa) +lemma iff_identities_le {W : MorphismProperty C} : + W.ContainsIdentities ↔ identities C ≤ W := + ⟨fun _ ↦ by intro _ _ _ ⟨_⟩; exact id_mem _, fun h ↦ ⟨fun _ ↦ h _ ⟨_⟩⟩⟩ + +instance : (identities C).ContainsIdentities := + iff_identities_le.2 (by rfl) + end ContainsIdentities instance Prod.containsIdentities {C₁ C₂ : Type*} [Category* C₁] [Category* C₂] @@ -220,6 +239,12 @@ instance : (epimorphisms C).IsMultiplicative where rw [epimorphisms.iff] at hf hg ⊢ apply epi_comp +instance : (identities C).IsMultiplicative where + comp_mem := by + rintro _ _ _ _ _ ⟨_⟩ ⟨_⟩ + simp only [Category.comp_id] + constructor + instance {P : MorphismProperty D} [P.IsMultiplicative] (F : C ⥤ D) : (P.inverseImage F).IsMultiplicative where diff --git a/Mathlib/CategoryTheory/MorphismProperty/Factorization.lean b/Mathlib/CategoryTheory/MorphismProperty/Factorization.lean index 616aca68551303..6c864a3443bd19 100644 --- a/Mathlib/CategoryTheory/MorphismProperty/Factorization.lean +++ b/Mathlib/CategoryTheory/MorphismProperty/Factorization.lean @@ -72,6 +72,25 @@ def op {X Y : C} {f : X ⟶ Y} (hf : MapFactorizationData W₁ W₂ f) : hi := hf.hp hp := hf.hi +/-- The factorization obtained from a factorization in the opposite category. -/ +@[simps] +protected def unop {W₁ W₂ : MorphismProperty Cᵒᵖ} {X Y : Cᵒᵖ} {f : X ⟶ Y} + (φ : MapFactorizationData W₁ W₂ f) : + MapFactorizationData W₂.unop W₁.unop f.unop where + Z := φ.Z.unop + i := φ.p.unop + p := φ.i.unop + hi := φ.hp + hp := φ.hi + fac := by simp [← unop_comp] + +/-- The bijection between factorizations in `C` and factorizations in `Cᵒᵖ`. -/ +@[simps] +def opEquiv {W₁ W₂ : MorphismProperty C} {X Y : C} {f : X ⟶ Y} : + MapFactorizationData W₁ W₂ f ≃ MapFactorizationData W₂.op W₁.op f.op where + toFun φ := φ.op + invFun φ := φ.unop + end MapFactorizationData /-- The data of a term in `MapFactorizationData W₁ W₂ f` for any morphism `f`. -/ diff --git a/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean b/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean index 8c5678c5a04da0..2f3a35a8d1f94f 100644 --- a/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean +++ b/Mathlib/CategoryTheory/MorphismProperty/IsInvertedBy.lean @@ -173,6 +173,11 @@ lemma IsInvertedBy.map_iff {C₁ C₂ C₃ : Type*} [Category* C₁] [Category* (W.map F).IsInvertedBy G ↔ W.IsInvertedBy (F ⋙ G) := by simp only [IsInvertedBy.iff_map_le_isomorphisms, map_map] +lemma isInvertedBy_isomorphisms (F : C ⥤ D) : (isomorphisms C).IsInvertedBy F := by + intro _ _ _ hf + simp only [isomorphisms.iff] at hf + infer_instance + end MorphismProperty end CategoryTheory diff --git a/Mathlib/CategoryTheory/MorphismProperty/Limits.lean b/Mathlib/CategoryTheory/MorphismProperty/Limits.lean index e23d976bc8ad8a..88affa6db43406 100644 --- a/Mathlib/CategoryTheory/MorphismProperty/Limits.lean +++ b/Mathlib/CategoryTheory/MorphismProperty/Limits.lean @@ -517,6 +517,7 @@ inductive colimitsOfShape : MorphismProperty C (h₁ : IsColimit c₁) (h₂ : IsColimit c₂) (f : X₁ ⟶ X₂) (_ : W.functorCategory J f) : colimitsOfShape (h₁.desc (Cocone.mk _ (f ≫ c₂.ι))) +set_option backward.isDefEq.respectTransparency false in variable {W J} in lemma colimitsOfShape.mk' (X₁ X₂ : J ⥤ C) (c₁ : Cocone X₁) (c₂ : Cocone X₂) (h₁ : IsColimit c₁) (h₂ : IsColimit c₂) (f : X₁ ⟶ X₂) (hf : W.functorCategory J f) @@ -599,6 +600,7 @@ class IsStableUnderColimitsOfShape : Prop where (h₁ : IsColimit c₁) (h₁ : IsColimit c₂) (f : X₁ ⟶ X₂) (_ : W.functorCategory J f) (φ : c₁.pt ⟶ c₂.pt) (hφ : ∀ j, c₁.ι.app j ≫ φ = f.app j ≫ c₂.ι.app j) : W φ +set_option backward.isDefEq.respectTransparency false in lemma isStableUnderColimitsOfShape_iff_colimitsOfShape_le : W.IsStableUnderColimitsOfShape J ↔ W.colimitsOfShape J ≤ W := by constructor @@ -668,6 +670,7 @@ lemma coproducts_of_small {X Y : C} (f : X ⟶ Y) {J : Type w'} rwa [← W.colimitsOfShape_eq_of_equivalence (Discrete.equivalence (equivShrink.{w} J))] set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in lemma le_colimitsOfShape_punit : W ≤ W.colimitsOfShape (Discrete PUnit.{w + 1}) := by intro X₁ X₂ f hf have h := initialIsInitial (C := Discrete (PUnit.{w + 1})) diff --git a/Mathlib/CategoryTheory/MorphismProperty/Local.lean b/Mathlib/CategoryTheory/MorphismProperty/Local.lean index ba30a655b47b9a..60a80b23c863ab 100644 --- a/Mathlib/CategoryTheory/MorphismProperty/Local.lean +++ b/Mathlib/CategoryTheory/MorphismProperty/Local.lean @@ -135,7 +135,13 @@ instance inf (P Q : MorphismProperty C) [IsLocalAtTarget P K] [IsLocalAtTarget Q end IsLocalAtTarget -alias of_zeroHypercover_target := IsLocalAtTarget.of_zeroHypercover +set_option backward.defeqAttrib.useBackward true in +lemma of_zeroHypercover_target {P : MorphismProperty C} {K : Precoverage C} [K.HasPullbacks] + [P.IsLocalAtTarget K] {X Y : C} {f : X ⟶ Y} (𝒰 : Precoverage.ZeroHypercover.{w} K Y) + [Precoverage.ZeroHypercover.Small.{v} 𝒰] (h : ∀ i, P (pullback.snd f (𝒰.f i))) : + P f := by + rw [IsLocalAtTarget.iff_of_zeroHypercover (P := P) 𝒰.restrictIndexOfSmall] + simp [h] alias iff_of_zeroHypercover_target := IsLocalAtTarget.iff_of_zeroHypercover @@ -211,7 +217,13 @@ instance inf (P Q : MorphismProperty C) [IsLocalAtSource P K] [IsLocalAtSource Q end IsLocalAtSource -alias of_zeroHypercover_source := IsLocalAtSource.of_zeroHypercover +set_option backward.defeqAttrib.useBackward true in +lemma of_zeroHypercover_source {P : MorphismProperty C} {K : Precoverage C} + [P.IsLocalAtSource K] {X Y : C} {f : X ⟶ Y} (𝒰 : Precoverage.ZeroHypercover.{w} K X) + [Precoverage.ZeroHypercover.Small.{v} 𝒰] (h : ∀ i, P (𝒰.f i ≫ f)) : + P f := by + rw [IsLocalAtSource.iff_of_zeroHypercover (P := P) 𝒰.restrictIndexOfSmall] + simp [h] alias iff_of_zeroHypercover_source := IsLocalAtSource.iff_of_zeroHypercover diff --git a/Mathlib/CategoryTheory/MorphismProperty/LocalClosure.lean b/Mathlib/CategoryTheory/MorphismProperty/LocalClosure.lean new file mode 100644 index 00000000000000..153ee9d616bd1c --- /dev/null +++ b/Mathlib/CategoryTheory/MorphismProperty/LocalClosure.lean @@ -0,0 +1,141 @@ +/- +Copyright (c) 2025 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Merten +-/ +module + +public import Mathlib.CategoryTheory.MorphismProperty.Local +public import Mathlib.CategoryTheory.Sites.MorphismProperty + +/-! +# Local closure of morphism properties + +We define the source local closure of a morphism property `P` w.r.t. a precoverage `K` as the +weakest property containing `P` that is `K`-local on the source. +-/ + +@[expose] public section + +universe w v u + +open CategoryTheory Limits MorphismProperty + +variable {C : Type u} [Category.{v} C] + +namespace CategoryTheory.MorphismProperty + +variable {K : Precoverage C} + +/-- The source-local closure of `P` along a precoverage `K` is the weakest property +containing `P` that is local on the source. -/ +inductive sourceLocalClosure (K : Precoverage C) (P : MorphismProperty C) : MorphismProperty C + /-- Force `P ≤ sourceLocalClosure K P`. -/ + | of {X Y : C} (f : X ⟶ Y) : P f → sourceLocalClosure K P f + /-- Force `RespectsIso`. -/ + | of_iso {X Y X' Y' : C} (f : X ⟶ Y) (g : X' ⟶ Y') (e : Arrow.mk f ≅ Arrow.mk g) : + sourceLocalClosure K P f → sourceLocalClosure K P g + | comp {X Y : C} (f : X ⟶ Y) (hf : sourceLocalClosure K P f) (R : Presieve X) (hR : R ∈ K X) + {U : C} (g : U ⟶ X) : R g → sourceLocalClosure K P (g ≫ f) + | of_presieve {X Y : C} (f : X ⟶ Y) (R : Presieve X) (hR : R ∈ K X) + (h : ∀ (U : C) (g : U ⟶ X), R g → sourceLocalClosure K P (g ≫ f)) : + sourceLocalClosure K P f + +namespace sourceLocalClosure + +attribute [grind .] of + +variable {P Q : MorphismProperty C} {X Y : C} + +instance : (sourceLocalClosure K P).IsLocalAtSource K where + precomp i hi f hf := .of_iso _ _ (Arrow.isoMk' _ _ (asIso i).symm (.refl _)) hf + postcomp i hi f hf := .of_iso _ _ (Arrow.isoMk' _ _ (.refl _) (asIso i)) hf + comp hR _ g hg hf := .comp _ hf _ hR _ hg + of_forall_comp hR h := .of_presieve _ _ hR h + +lemma le : P ≤ sourceLocalClosure K P := + fun _ _ _ ↦ .of _ + +lemma le_of_isLocalAtSource (h : P ≤ Q) [Q.IsLocalAtSource K] : sourceLocalClosure K P ≤ Q := by + intro X Y f hf + induction hf with + | of f hf => exact h _ hf + | of_iso f g e _ hf => rwa [Q.arrow_mk_iso_iff e.symm] + | comp f hf R hR g hg ih => apply IsLocalAtSource.comp hR _ hg ih + | of_presieve f R hR h ih => apply IsLocalAtSource.of_forall_comp hR fun U g hg ↦ ih _ _ hg + +instance [P.ContainsIdentities] : ContainsIdentities (sourceLocalClosure K P) where + id_mem _ := le _ (P.id_mem _) + +set_option backward.isDefEq.respectTransparency false in +instance [P.IsStableUnderBaseChange] [K.IsStableUnderBaseChange] [HasPullbacks C] : + IsStableUnderBaseChange (sourceLocalClosure K P) where + of_isPullback {Y} X W Z g f fst snd h hf := by + induction hf generalizing W snd with + | of f' hf' => exact .of _ (P.of_isPullback h hf') + | of_iso f' g' e hf' ih => + exact ih _ (g ≫ e.inv.right) (fst ≫ e.inv.left) _ (h.paste_horiz (.of_horiz_isIso ⟨e.inv.w⟩)) + | comp f' hf' R hR g' hg' ih => + let u : W ⟶ pullback g f' := pullback.lift snd (fst ≫ g') (by simp [h.w.symm]) + have : snd = u ≫ pullback.fst g f' := by simp [u] + rw [this] at h ⊢ + let e : W ≅ pullback g' (pullback.snd g f') := + IsPullback.isoPullback (.of_bot h (by simp [u]) (.flip <| .of_hasPullback _ _)) + rw [← (sourceLocalClosure K P).cancel_left_of_respectsIso e.inv, ← Category.assoc] + refine .comp _ (ih _ _ _ _ (.flip (.of_hasPullback _ _))) _ + (K.pullbackArrows_mem (pullback.snd _ _) hR) _ ?_ + simpa [e, u] using .mk _ _ hg' + | of_presieve f R hR h ih => + refine .of_presieve _ _ (K.pullbackArrows_mem fst hR) ?_ + intro U v ⟨Z, u, hu⟩ + exact ih _ _ hu _ g (pullback.fst _ _) _ (.paste_vert (.of_hasPullback _ _) h) + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +lemma sourceLocalClosure_iff_of_respectsLeft [P.RespectsIso] [P.RespectsLeft K.morphismProperty] + [K.HasIsos] [K.IsStableUnderBaseChange] [K.IsStableUnderComposition] [K.HasPullbacks] {X Y : C} + {f : X ⟶ Y} : + sourceLocalClosure K P f ↔ ∃ R ∈ K X, ∀ (U : C) (g : U ⟶ X), R g → P (g ≫ f) := by + refine ⟨?_, ?_⟩ + · intro h + induction h with + | of f hf => exact ⟨.singleton (𝟙 _), K.mem_coverings_of_isIso _, fun U g ⟨⟩ ↦ by simpa⟩ + | of_iso f g e hf h => + obtain ⟨R, hR, h⟩ := h + rw [K.mem_iff_exists_zeroHypercover] at hR + obtain ⟨E, rfl⟩ := hR + refine ⟨_, (E.pushforward e.hom.left (K.mem_coverings_of_isIso _)).mem₀, ?_⟩ + intro U v ⟨i⟩ + dsimp + simp only [Category.assoc, Arrow.w_mk_right, Arrow.mk_left, Arrow.mk_right, Arrow.mk_hom] + rw [← Category.assoc, P.cancel_right_of_respectsIso] + exact h _ _ ⟨i⟩ + | comp f hf R hR g hg ih => + obtain ⟨S, hS, h⟩ := ih + rw [K.mem_iff_exists_zeroHypercover] at hS hR + obtain ⟨E, rfl⟩ := hS + obtain ⟨F, rfl⟩ := hR + refine ⟨(E.pullback₁ g).presieve₀, (E.pullback₁ g).mem₀, ?_⟩ + intro U v ⟨i⟩ + dsimp + rw [pullback.condition_assoc] + refine RespectsLeft.precomp (Q := K.morphismProperty) _ ?_ _ ?_ + · obtain ⟨j⟩ := hg + exact (F.pullback₂ (E.f i)).morphismProperty j + · exact h _ _ ⟨i⟩ + | of_presieve f R hR h ih => + rw [K.mem_iff_exists_zeroHypercover] at hR + obtain ⟨E, rfl⟩ := hR + choose S hS h' using fun i : E.I₀ ↦ ih _ _ ⟨i⟩ + simp_rw [K.mem_iff_exists_zeroHypercover] at hS + choose F hF using hS + refine ⟨_, (E.bind F).mem₀, fun U g ⟨j⟩ ↦ ?_⟩ + dsimp + rw [Category.assoc] + exact h' _ _ _ (by simp [hF]) + · intro ⟨R, hR, h⟩ + exact .of_presieve _ _ hR (by grind) + +end sourceLocalClosure + +end CategoryTheory.MorphismProperty diff --git a/Mathlib/CategoryTheory/NatIso.lean b/Mathlib/CategoryTheory/NatIso.lean index 6737823ba25dc2..dac61bd6dd46be 100644 --- a/Mathlib/CategoryTheory/NatIso.lean +++ b/Mathlib/CategoryTheory/NatIso.lean @@ -55,7 +55,6 @@ def app {F G : C ⥤ D} (α : F ≅ G) (X : C) : hom := α.hom.app X inv := α.inv.app X -set_option linter.existingAttributeWarning false in attribute [to_dual existing app_inv] app_hom @[reassoc +to_dual (attr := simp), grind =] @@ -191,11 +190,9 @@ def ofComponents (app : ∀ X : C, F.obj X ≅ G.obj X) exact h } set_option linter.translateOverwrite false in -set_option linter.existingAttributeWarning false in attribute [to_dual existing ofComponents'_inv_app] ofComponents_hom_app set_option linter.translateOverwrite false in -set_option linter.existingAttributeWarning false in attribute [to_dual existing ofComponents'_hom_app] ofComponents_inv_app @[to_dual (attr := simp)] @@ -214,7 +211,6 @@ def hcomp {F G : C ⥤ D} {H I : D ⥤ E} (α : F ≅ G) (β : H ≅ I) : F ⋙ hom := α.hom ◫ β.hom inv := α.inv ◫ β.inv -set_option linter.existingAttributeWarning false in attribute [to_dual existing hcomp_inv] hcomp_hom @[to_dual self] diff --git a/Mathlib/CategoryTheory/ObjectProperty/Comma.lean b/Mathlib/CategoryTheory/ObjectProperty/Comma.lean new file mode 100644 index 00000000000000..d44f62cc8caa94 --- /dev/null +++ b/Mathlib/CategoryTheory/ObjectProperty/Comma.lean @@ -0,0 +1,69 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.CategoryTheory.ObjectProperty.Retract + +/-! +# Properties of objects in comma categories + +-/ + +universe w + +@[expose] public section + +namespace CategoryTheory.ObjectProperty + +variable {C₁ C₂ D : Type*} [Category* C₁] [Category* C₂] [Category* D] + (F₁ : C₁ ⥤ D) (F₂ : C₂ ⥤ D) + (P₁ : ObjectProperty C₁) (P₂ : ObjectProperty C₂) + +/-- Given functors `F₁ : C₁ ⥤ D` and `F₂ : C₂ ⥤ D`, and properties +of objects `P₁ : ObjectProperty C₁` and `P₂ : ObjectProperty C₂`, +this is the property of objects in `Comma F₁ F₂` satisfying +by the objects corresponding to morphisms `F₁.obj X₁ ⟶ F₂.obj X₂` +where `P₁ X₁` and `P₂ X₂` hold. -/ +def comma : ObjectProperty (Comma F₁ F₂) := + P₁.inverseImage (Comma.fst _ _) ⊓ P₂.inverseImage (Comma.snd _ _) + +variable {F₁ F₂} in +@[simp] +lemma comma_iff (X : Comma F₁ F₂) : + comma F₁ F₂ P₁ P₂ X ↔ P₁ X.left ∧ P₂ X.right := Iff.rfl + +instance [P₁.IsStableUnderRetracts] [P₂.IsStableUnderRetracts] : + (comma F₁ F₂ P₁ P₂).IsStableUnderRetracts where + of_retract r h := + ⟨P₁.prop_of_retract (r.map (Comma.fst _ _)) h.1, + P₂.prop_of_retract (r.map (Comma.snd _ _)) h.2⟩ + +instance [P₁.IsClosedUnderIsomorphisms] [P₂.IsClosedUnderIsomorphisms] : + (comma F₁ F₂ P₁ P₂).IsClosedUnderIsomorphisms where + of_iso e h := + ⟨P₁.prop_of_iso ((Comma.fst _ _).mapIso e) h.1, + P₂.prop_of_iso ((Comma.snd _ _).mapIso e) h.2⟩ + +instance [ObjectProperty.Small.{w} P₁] [ObjectProperty.Small.{w} P₂] [LocallySmall.{w} D] : + ObjectProperty.Small.{w} (comma F₁ F₂ P₁ P₂) := + small_of_surjective + (α := Σ (X₁ : Subtype P₁) (X₂ : Subtype P₂), F₁.obj X₁.val ⟶ F₂.obj X₂.val) + (f := fun ⟨X₁, X₂, f⟩ ↦ ⟨Comma.mk _ _ f, X₁.prop, X₂.prop⟩) + (fun f ↦ ⟨⟨⟨_, f.prop.1⟩, ⟨_, f.prop.2⟩, f.val.hom⟩, rfl⟩) + +instance [ObjectProperty.EssentiallySmall.{w} P₁] + [ObjectProperty.EssentiallySmall.{w} P₂] [LocallySmall.{w} D] : + ObjectProperty.EssentiallySmall.{w} (comma F₁ F₂ P₁ P₂) := by + obtain ⟨Q₁, _, h₁, h₁'⟩ := EssentiallySmall.exists_small_le.{w} P₁ + obtain ⟨Q₂, _, h₂, h₂'⟩ := EssentiallySmall.exists_small_le.{w} P₂ + refine ⟨comma F₁ F₂ Q₁ Q₂, inferInstance, fun f hf ↦ ?_⟩ + simp only [comma_iff] at hf + obtain ⟨X₁, hX₁, ⟨e₁⟩⟩ := h₁' _ hf.1 + obtain ⟨X₂, hX₂, ⟨e₂⟩⟩ := h₂' _ hf.2 + exact ⟨Comma.mk _ _ (F₁.map e₁.inv ≫ f.hom ≫ F₂.map e₂.hom), by tauto, + ⟨Comma.isoMk e₁ e₂⟩⟩ + +end CategoryTheory.ObjectProperty diff --git a/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean b/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean index 51dd57c087bcb7..6152f84f2e3d72 100644 --- a/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean +++ b/Mathlib/CategoryTheory/ObjectProperty/CompleteLattice.lean @@ -97,6 +97,13 @@ instance [∀ a, (P a).IsClosedUnderIsomorphisms] : simp only [isClosedUnderIsomorphisms_iff_isoClosure_eq_self, isoClosure_iSup, isoClosure_eq_self] +instance [∀ a, (P a).IsClosedUnderIsomorphisms] : + ((⨅ (a : α), P a)).IsClosedUnderIsomorphisms where + of_iso e h := by + simp only [iInf_apply, iInf_Prop_eq] at h ⊢ + intro a + exact (P a).prop_of_iso e (h a) + end @[push] diff --git a/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean b/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean index 7bdf56218c1788..a006f41f609248 100644 --- a/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean +++ b/Mathlib/CategoryTheory/ObjectProperty/Extensions.lean @@ -52,7 +52,7 @@ instance : (⊤ : ObjectProperty C).IsClosedUnderExtensions where instance : IsClosedUnderExtensions (IsZero (C := C)) where prop_X₂_of_shortExact hS h₁ h₃ := - hS.exact.isZero_of_both_zeros (h₁.eq_of_src _ _) (h₃.eq_of_tgt _ _) + hS.exact.isZero_of_both_isZero h₁ h₃ instance [P.IsClosedUnderExtensions] (F : D ⥤ C) [HasZeroMorphisms D] [F.PreservesZeroMorphisms] diff --git a/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean b/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean index a160c062fe8b4a..cceb19b3174f4c 100644 --- a/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean +++ b/Mathlib/CategoryTheory/ObjectProperty/FunctorCategory/PreservesLimits.lean @@ -45,6 +45,9 @@ lemma congr_preservesLimit {F F' : K ⥤ J} (e : F ≅ F') : exact ⟨fun h ↦ preservesLimit_of_iso_diagram _ e, fun h ↦ preservesLimit_of_iso_diagram _ e.symm⟩ +instance (F : K ⥤ J) : (preservesLimit (C := C) F).IsClosedUnderIsomorphisms where + of_iso e _ := preservesLimit_of_natIso _ e + variable {K} in /-- The property of objects in the functor category `J ⥤ C` which preserves the colimit of a functor `F : K ⥤ J`. -/ @@ -61,6 +64,9 @@ lemma congr_preservesColimit {F F' : K ⥤ J} (e : F ≅ F') : exact ⟨fun h ↦ preservesColimit_of_iso_diagram _ e, fun h ↦ preservesColimit_of_iso_diagram _ e.symm⟩ +instance (F : K ⥤ J) : (preservesColimit (C := C) F).IsClosedUnderIsomorphisms where + of_iso e _ := preservesColimit_of_natIso _ e + /-- The property of objects in the functor category `J ⥤ C` which preserves limits of shape `K`. -/ abbrev preservesLimitsOfShape : ObjectProperty (J ⥤ C) := PreservesLimitsOfShape K @@ -84,6 +90,10 @@ lemma congr_preservesLimitsOfShape (e : K ≌ K') : exact ⟨fun _ ↦ preservesLimitsOfShape_of_equiv e _, fun _ ↦ preservesLimitsOfShape_of_equiv e.symm _⟩ +instance : (preservesLimitsOfShape (J := J) (C := C) K).IsClosedUnderIsomorphisms := by + rw [preservesLimitsOfShape_eq_iSup] + infer_instance + /-- The property of objects in the functor category `J ⥤ C` which preserves colimits of shape `K`. -/ abbrev preservesColimitsOfShape : ObjectProperty (J ⥤ C) := PreservesColimitsOfShape K @@ -107,6 +117,10 @@ lemma congr_preservesColimitsOfShape (e : K ≌ K') : exact ⟨fun _ ↦ preservesColimitsOfShape_of_equiv e _, fun _ ↦ preservesColimitsOfShape_of_equiv e.symm _⟩ +instance : (preservesColimitsOfShape (J := J) (C := C) K).IsClosedUnderIsomorphisms := by + rw [preservesColimitsOfShape_eq_iSup] + infer_instance + /-- The property of objects in the functor category `J ⥤ C` which preserves finite limits. -/ abbrev preservesFiniteLimits : ObjectProperty (J ⥤ C) := PreservesFiniteLimits @@ -115,10 +129,16 @@ abbrev preservesFiniteLimits : ObjectProperty (J ⥤ C) := PreservesFiniteLimits lemma preservesFiniteLimits_iff (F : J ⥤ C) : preservesFiniteLimits F ↔ PreservesFiniteLimits F := Iff.rfl +instance : (preservesFiniteLimits (J := J) (C := C)).IsClosedUnderIsomorphisms where + of_iso e _ := preservesFiniteLimits_of_natIso e + /-- The property of objects in the functor category `J ⥤ C` which preserves finite colimits. -/ abbrev preservesFiniteColimits : ObjectProperty (J ⥤ C) := PreservesFiniteColimits +instance : (preservesFiniteColimits (J := J) (C := C)).IsClosedUnderIsomorphisms where + of_iso e _ := preservesFiniteColimits_of_natIso e + @[simp] lemma preservesFiniteColimits_iff (F : J ⥤ C) : preservesFiniteColimits F ↔ PreservesFiniteColimits F := Iff.rfl diff --git a/Mathlib/CategoryTheory/PathCategory/Basic.lean b/Mathlib/CategoryTheory/PathCategory/Basic.lean index ad3b72ce3b1c92..335601a33bc996 100644 --- a/Mathlib/CategoryTheory/PathCategory/Basic.lean +++ b/Mathlib/CategoryTheory/PathCategory/Basic.lean @@ -9,6 +9,7 @@ public import Mathlib.CategoryTheory.Quotient /-! # The category paths on a quiver. + When `C` is a quiver, `paths C` is the category of paths. ## When the quiver is itself a category diff --git a/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean b/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean index 364ab457c95d6e..c8554346bf6ea6 100644 --- a/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean +++ b/Mathlib/CategoryTheory/Preadditive/AdditiveFunctor.lean @@ -218,6 +218,12 @@ instance (priority := 100) preservesFiniteProductsOfAdditive [Additive F] : PreservesFiniteProducts F where preserves _ := preservesProductsOfShape_of_preservesBiproductsOfShape F +lemma hasFiniteProducts_of_additive_of_essSurj [HasFiniteProducts C] [Additive F] + [EssSurj F] : HasFiniteProducts D := + ⟨fun _ ↦ ⟨fun K ↦ hasLimit_of_iso + (F := Discrete.functor (fun i ↦ F.objPreimage (K.obj ⟨i⟩)) ⋙ F) + (Discrete.natIso (fun _ ↦ F.objObjPreimageIso _))⟩⟩ + theorem additive_of_preservesBinaryBiproducts [HasBinaryBiproducts C] [PreservesZeroMorphisms F] [PreservesBinaryBiproducts F] : Additive F where map_add {X Y f g} := by diff --git a/Mathlib/CategoryTheory/Preadditive/Comma.lean b/Mathlib/CategoryTheory/Preadditive/Comma.lean new file mode 100644 index 00000000000000..dc477de5a32189 --- /dev/null +++ b/Mathlib/CategoryTheory/Preadditive/Comma.lean @@ -0,0 +1,130 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Preadditive.AdditiveFunctor + +/-! +# The comma category is preadditive + +If we have additive functors `L : A ⥤ T` and `R : B ⥤ T` between preadditive categories, +then there is a structure of preadditive category on `Comma L R` such that addition commutes +with the left and right projections. + +We then apply this to `Arrow T` for `T` a preadditive category. + +## Tags + +comma, arrow, preadditive +-/ + +@[expose] public section + +namespace CategoryTheory + +open Category + +universe v₁ v₂ v₃ u₁ u₂ u₃ + +variable {A : Type u₁} [Category.{v₁} A] [Preadditive A] +variable {B : Type u₂} [Category.{v₂} B] [Preadditive B] +variable {T : Type u₃} [Category.{v₃} T] [Preadditive T] +variable (L : A ⥤ T) [L.Additive] (R : B ⥤ T) [R.Additive] +variable {u v : Comma L R} + +section Comma + +namespace CommaMorphism + +@[simps!] +instance : Add (u ⟶ v) where + add α β := CommaMorphism.mk (α.left + β.left) (α.right + β.right) (by simp) + +@[simps!] +instance : Sub (u ⟶ v) where + sub α β := CommaMorphism.mk (α.left - β.left) (α.right - β.right) (by simp) + +@[simps!] +instance : Zero (u ⟶ v) where + zero := CommaMorphism.mk 0 0 + +@[simps!] +instance : Neg (u ⟶ v) where + neg α := CommaMorphism.mk (-α.left) (-α.right) + +end CommaMorphism + +instance : AddCommGroup (u ⟶ v) where + add_assoc _ _ _ := by ext <;> simp [add_assoc] + zero_add _ := by cat_disch + add_zero _ := by cat_disch + add_comm _ _ := by ext <;> simp [add_comm] + neg_add_cancel _ := by cat_disch + sub_eq_add_neg _ _ := by ext <;> simp [sub_eq_add_neg] + nsmul n α := CommaMorphism.mk (n • α.left) (n • α.right) + (by simp [Functor.map_nsmul, Preadditive.comp_nsmul, Preadditive.nsmul_comp]) + zsmul n α := CommaMorphism.mk (n • α.left) (n • α.right) + (by simp [Functor.map_zsmul, Preadditive.comp_zsmul, Preadditive.zsmul_comp]) + nsmul_zero := by simp_rw [HSMul.hSMul, SMul.smul]; cat_disch + nsmul_succ _ _ := by simp_rw [HSMul.hSMul, SMul.smul]; ext <;> dsimp <;> simp [add_nsmul] + zsmul_zero' := by simp_rw [HSMul.hSMul, SMul.smul]; cat_disch + zsmul_succ' _ _ := by simp_rw [HSMul.hSMul, SMul.smul]; ext <;> dsimp <;> simp [add_zsmul] + zsmul_neg' _ _ := by + simp_rw [HSMul.hSMul, SMul.smul] + ext <;> dsimp <;> simp [add_nsmul, add_zsmul] + +/-- If we have additive functors `L : A ⥤ T` and `R : B ⥤ T` between preadditive categories, +then the category `Comma L R` is preadditive. +-/ +instance : Preadditive (Comma L R) where + +instance : (Comma.fst L R).Additive where + +instance : (Comma.snd L R).Additive where + +end Comma + +section Arrow + +/-- If a category `T` is preadditive, then so is its category of arrows. +-/ +instance : Preadditive (Arrow T) := inferInstanceAs (Preadditive (Comma (𝟭 T) (𝟭 T))) + +instance : (Arrow.leftFunc (C := T)).Additive := + inferInstanceAs ((Comma.fst (𝟭 T) (𝟭 T))).Additive + +instance : (Arrow.rightFunc (C := T)).Additive := + inferInstanceAs ((Comma.snd (𝟭 T) (𝟭 T))).Additive + +variable {u v : Arrow T} + +@[simp] +lemma Arrow.Hom.add_left (α β : u ⟶ v) : (α + β).left = α.left + β.left := rfl + +@[simp] +lemma Arrow.Hom.add_right (α β : u ⟶ v) : (α + β).right = α.right + β.right := rfl + +@[simp] +lemma Arrow.Hom.sub_left (α β : u ⟶ v) : (α - β).left = α.left - β.left := rfl + +@[simp] +lemma Arrow.Hom.sub_right (α β : u ⟶ v) : (α - β).right = α.right - β.right := rfl + +@[simp] +lemma Arrow.Hom.zero_left : (0 : u ⟶ v).left = 0 := rfl + +@[simp] +lemma Arrow.Hom.zero_right : (0 : u ⟶ v).right = 0 := rfl + +@[simp] +lemma Arrow.Hom.neg_left (α : u ⟶ v) : (-α).left = -α.left := rfl + +@[simp] +lemma Arrow.Hom.neg_right (α : u ⟶ v) : (-α).right = -α.right := rfl + +end Arrow + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Preadditive/FreydCategory/Homotopy.lean b/Mathlib/CategoryTheory/Preadditive/FreydCategory/Homotopy.lean new file mode 100644 index 00000000000000..c62a21ae54c0ce --- /dev/null +++ b/Mathlib/CategoryTheory/Preadditive/FreydCategory/Homotopy.lean @@ -0,0 +1,228 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Quotient +public import Mathlib.CategoryTheory.Preadditive.Comma + +/-! +# Homotopies in the arrow category + +We define left and right homotopies between morphisms of `Arrow V`, where `V` is +a preadditive category. + +TODO: Define the preadditive categories `LeftFreyd V` (resp. `RightFreyd V`) obtained by +taking the quotient of `Arrow V` by the left (resp. right) homotopy relation. If `V` +has binary biproducts, this will have all kernels (resp. cokernels) and will be the +category obtained by freely adjoining kernels (resp. cokernels) to `V`. + +-/ + +@[expose] public section + +noncomputable section + +open CategoryTheory Category + +variable {V : Type*} [Category* V] [Preadditive V] + +namespace CategoryTheory.Arrow + +variable {u v w : Arrow V} (f g : u ⟶ v) + +/-- A left homotopy on morphisms in the category of arrows of a preadditive category. -/ +@[ext] +structure LeftHomotopy where +/-- A "diagonal" morphism from the right object of `u` to the left object of `v`. -/ + hom : u.right ⟶ v.left +/-- The difference of the left morphisms factors through `hom`. -/ + comm : f.left - g.left = u.hom ≫ hom := by cat_disch + +/-- A right homotopy on morphisms in the category of arrows of a preadditive category. -/ +@[ext] +structure RightHomotopy where + /-- A "diagonal" morphism from the right object of `u` to the left object of `v`. -/ + hom : u.right ⟶ v.left + /-- The difference of the right morphisms factors through `hom`. -/ + comm : f.right - g.right = hom ≫ v.hom := by cat_disch + +variable {f g} + +namespace LeftHomotopy + +/-- `f` is left homotopic to `g` iff `f - g` is left homotopic to `0`. -/ +def equivSubZero : LeftHomotopy f g ≃ LeftHomotopy (f - g) 0 where + toFun h := + { hom := h.hom + comm := by simp [← h.comm]} + invFun h := + { hom := h.hom + comm := by simp [← h.comm]} + left_inv := by cat_disch + right_inv := by cat_disch + +/-- Equal maps of arrows are left homotopic. -/ +@[simps] +def ofEq (h : f = g) : LeftHomotopy f g where + hom := 0 + +/-- Every map of arrows is left homotopic to itself. -/ +@[simps!, refl] +def refl (f : u ⟶ v) : LeftHomotopy f f := + ofEq (rfl : f = f) + +/-- `f` is left homotopic to `g` iff `g` is left homotopic to `f`. -/ +@[simps!, symm] +def symm {f g : u ⟶ v} (h : LeftHomotopy f g) : LeftHomotopy g f where + hom := -h.hom + comm := by simp [← h.comm] + +/-- Left homotopy is a transitive relation. -/ +@[simps!, trans] +def trans {e f g : u ⟶ v} (h : LeftHomotopy e f) (k : LeftHomotopy f g) : LeftHomotopy e g where + hom := h.hom + k.hom + comm := by simp [← h.comm, ← k.comm] + +/-- The sum of two left homotopies is a left homotopy between the sum of the respective +morphisms. -/ +@[simps!] +def add {f₁ g₁ f₂ g₂ : u ⟶ v} (h₁ : LeftHomotopy f₁ g₁) (h₂ : LeftHomotopy f₂ g₂) : + LeftHomotopy (f₁ + f₂) (g₁ + g₂) where + hom := h₁.hom + h₂.hom + comm := by simp [← h₁.comm, ← h₂.comm, add_sub_add_comm] + +/-- Left homotopy is closed under composition (on the right). -/ +@[simps] +def compRight {e f : u ⟶ v} (h : LeftHomotopy e f) (g : v ⟶ w) : + LeftHomotopy (e ≫ g) (f ≫ g) where + hom := h.hom ≫ g.left + comm := by simp [← reassoc_of% h.comm] + +/-- Left homotopy is closed under composition (on the left). -/ +@[simps] +def compLeft {f g : v ⟶ w} (h : LeftHomotopy f g) (e : u ⟶ v) : + LeftHomotopy (e ≫ f) (e ≫ g) where + hom := e.right ≫ h.hom + comm := by simp [← reassoc_of% e.w, ← h.comm] + +/-- Left homotopy is closed under composition. -/ +@[simps!] +def comp {f₁ g₁ : u ⟶ v} {f₂ g₂ : v ⟶ w} + (h₁ : LeftHomotopy f₁ g₁) (h₂ : LeftHomotopy f₂ g₂) : LeftHomotopy (f₁ ≫ f₂) (g₁ ≫ g₂) := + (h₁.compRight _).trans (h₂.compLeft _) + +/-- A variant of `LeftHomotopy.compRight` useful for dealing with homotopy equivalences. -/ +@[simps!] +def compRightId {f : u ⟶ u} (h : LeftHomotopy f (𝟙 u)) (g : u ⟶ v) : LeftHomotopy (f ≫ g) g := + (h.compRight g).trans (ofEq <| id_comp _) + +/-- A variant of `LeftHomotopy.compLeft` useful for dealing with homotopy equivalences. -/ +@[simps!] +def compLeftId {f : v ⟶ v} (h : LeftHomotopy f (𝟙 v)) (g : u ⟶ v) : LeftHomotopy (g ≫ f) g := + (h.compLeft g).trans (ofEq <| comp_id _) + +end LeftHomotopy + +namespace RightHomotopy + +/-- `f` is right homotopic to `g` iff `f - g` is righthomotopic to `0`. -/ +def equivSubZero : RightHomotopy f g ≃ RightHomotopy (f - g) 0 where + toFun h := + { hom := h.hom + comm := by simp [← h.comm]} + invFun h := + { hom := h.hom + comm := by simp [← h.comm]} + left_inv := by cat_disch + right_inv := by cat_disch + +/-- Equal maps of arrows are right homotopic. -/ +@[simps] +def ofEq (h : f = g) : RightHomotopy f g where + hom := 0 + +/-- Every map of arrows is right homotopic to itself. -/ +@[simps!, refl] +def refl (f : u ⟶ v) : RightHomotopy f f := + ofEq (rfl : f = f) + +/-- `f` is right homotopic to `g` iff `g` is right homotopic to `f`. -/ +@[simps!, symm] +def symm {f g : u ⟶ v} (h : RightHomotopy f g) : RightHomotopy g f where + hom := -h.hom + comm := by simp [← h.comm] + +/-- Right homotopy is a transitive relation. -/ +@[simps!, trans] +def trans {e f g : u ⟶ v} (h : RightHomotopy e f) (k : RightHomotopy f g) : RightHomotopy e g where + hom := h.hom + k.hom + comm := by simp [← h.comm, ← k.comm] + +/-- The sum of two right homotopies is a right homotopy between the sum of the respective +morphisms. -/ +@[simps!] +def add {f₁ g₁ f₂ g₂ : u ⟶ v} (h₁ : RightHomotopy f₁ g₁) (h₂ : RightHomotopy f₂ g₂) : + RightHomotopy (f₁ + f₂) (g₁ + g₂) where + hom := h₁.hom + h₂.hom + comm := by simp [← h₁.comm, ← h₂.comm, add_sub_add_comm] + +/-- Right homotopy is closed under composition (on the right). -/ +@[simps] +def compRight {e f : u ⟶ v} (h : RightHomotopy e f) (g : v ⟶ w) : + RightHomotopy (e ≫ g) (f ≫ g) where + hom := h.hom ≫ g.left + comm := by simp [← reassoc_of% h.comm] + +/-- Right homotopy is closed under composition (on the left). -/ +@[simps] +def compLeft {f g : v ⟶ w} (h : RightHomotopy f g) (e : u ⟶ v) : + RightHomotopy (e ≫ f) (e ≫ g) where + hom := e.right ≫ h.hom + comm := by simp [← h.comm] + +/-- Right homotopy is closed under composition. -/ +@[simps!] +def comp {f₁ g₁ : u ⟶ v} {f₂ g₂ : v ⟶ w} + (h₁ : RightHomotopy f₁ g₁) (h₂ : RightHomotopy f₂ g₂) : RightHomotopy (f₁ ≫ f₂) (g₁ ≫ g₂) := + (h₁.compRight _).trans (h₂.compLeft _) + +/-- A variant of `RightHomotopy.compRight` useful for dealing with homotopy equivalences. -/ +@[simps!] +def compRightId {f : u ⟶ u} (h : RightHomotopy f (𝟙 u)) (g : u ⟶ v) : RightHomotopy (f ≫ g) g := + (h.compRight g).trans (ofEq <| id_comp _) + +/-- A variant of `RightHomotopy.compLeft` useful for dealing with homotopy equivalences. -/ +@[simps!] +def compLeftId {f : v ⟶ v} (h : RightHomotopy f (𝟙 v)) (g : u ⟶ v) : RightHomotopy (g ≫ f) g := + (h.compLeft g).trans (ofEq <| comp_id _) + +end RightHomotopy + +variable (V) + +/-- The left homotopy relation on morphisms of `Arrow V`. -/ +def leftHomotopic : HomRel (Arrow V) := fun _ _ f g => Nonempty (LeftHomotopy f g) + +instance leftHomotopy_congruence : Congruence (leftHomotopic V) where + equivalence := + { refl := fun C => ⟨LeftHomotopy.refl C⟩ + symm := fun ⟨w⟩ => ⟨w.symm⟩ + trans := fun ⟨w₁⟩ ⟨w₂⟩ => ⟨w₁.trans w₂⟩ } + comp_left := fun _ _ _ ⟨i⟩ => ⟨i.compLeft _⟩ + comp_right := fun _ ⟨i⟩ => ⟨i.compRight _⟩ + +/-- The left homotopy relation on morphisms of `Arrow V`. -/ +def rightHomotopic : HomRel (Arrow V) := fun _ _ f g => Nonempty (RightHomotopy f g) + +instance rightHomotopy_congruence : Congruence (rightHomotopic V) where + equivalence := + { refl := fun C => ⟨RightHomotopy.refl C⟩ + symm := fun ⟨w⟩ => ⟨w.symm⟩ + trans := fun ⟨w₁⟩ ⟨w₂⟩ => ⟨w₁.trans w₂⟩ } + comp_left := fun _ _ _ ⟨i⟩ => ⟨i.compLeft _⟩ + comp_right := fun _ ⟨i⟩ => ⟨i.compRight _⟩ + +end CategoryTheory.Arrow diff --git a/Mathlib/CategoryTheory/Preadditive/FreydCategory/RightFreyd.lean b/Mathlib/CategoryTheory/Preadditive/FreydCategory/RightFreyd.lean new file mode 100644 index 00000000000000..279b5749275e32 --- /dev/null +++ b/Mathlib/CategoryTheory/Preadditive/FreydCategory/RightFreyd.lean @@ -0,0 +1,213 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Preadditive.FreydCategory.Homotopy +public import Mathlib.CategoryTheory.Quotient.Preadditive + +/-! +# The right Freyd category + +Let `V` be a preadditive category. The right Freyd category of `V` is the quotient of +`Arrow V` by the right homotopy relation. (This is simply called "Freyd category" +in the reference.) This is a preadditive category with a fully +faithful additive functor `RightFreyd.functor : V ⥤ RightFreyd V`. + +We also show that, if `V` has binary biproducts, then `RightFreyd V` has cokernels. In fact +we construct, given a morphism `f : u ⟶ v` in `Arrow V`, a morphism +`Candidate.π f : v ⟶ Candidate.cokernel f` in `Arrow V` such that +`f ≫ Candidate.π f` is right homotopic to `0` (see `Candidate.condition`). +This allows us to define a cokernel cofork for `(quotient V).map f` (see +`Candidate.cokernelCofork`), and we show in `Candidate.isColimitCokernelCofork` that this is +a cokernel cofork. + +## References +* [Posur, S., *A constructive approach to Freyd categories*][posur2021Freyd] + +-/ + +@[expose] public section + +noncomputable section + +open CategoryTheory Category Limits Arrow + +variable (V : Type*) [Category* V] [Preadditive V] + +namespace CategoryTheory.Preadditive + +/-- If `V` is a preadditive category, then `RightFreyd V` is the category of arrows in `V`, +with morphisms identified when they are right homotopic. -/ +def RightFreyd := + CategoryTheory.Quotient (rightHomotopic V) + +instance : Category (RightFreyd V) := + inferInstanceAs <| Category (CategoryTheory.Quotient (rightHomotopic V)) + +/-- The category `RightFreyd V` is preadditive. -/ +instance : Preadditive (RightFreyd V) := + Quotient.preadditive _ (by + rintro _ _ _ _ _ _ ⟨h⟩ ⟨h'⟩ + exact ⟨RightHomotopy.add h h'⟩) + +namespace RightFreyd + +/-- The quotient functor from `Arrow V` to `RightFreyd V`. -/ +def quotient : Arrow V ⥤ RightFreyd V := + CategoryTheory.Quotient.functor _ + +instance : (quotient V).Full := Quotient.full_functor _ + +instance : (quotient V).EssSurj := Quotient.essSurj_functor _ + +instance : (quotient V).Additive where + +variable {V} + +/-- If two morphisms in `Arrow V` are right homotopic, then they become equal in the right +Freyd category. -/ +theorem eq_of_rightHomotopy {u v : Arrow V} (f g : u ⟶ v) (h : RightHomotopy f g) : + (quotient V).map f = (quotient V).map g := + CategoryTheory.Quotient.sound _ ⟨h⟩ + +/-- If two morphisms of `Arrow V` become equal in the right Freyd category, +then they are right homotopic. -/ +def homotopyOfEq {u v : Arrow V} (f g : u ⟶ v) + (w : (quotient V).map f = (quotient V).map g) : RightHomotopy f g := + ((Quotient.functor_map_eq_iff _ _ _).mp w).some + +variable {u v : Arrow V} (f g : u ⟶ v) + +/-- Two morphisms in `Arrow V` have the same image in `RightFreyd V` if and only if there +exists a right homotopy between them. -/ +lemma quotient_map_eq_iff : + (quotient V).map f = (quotient V).map g ↔ Nonempty (RightHomotopy f g) := + ⟨fun h ↦ ⟨homotopyOfEq _ _ (by simpa using h)⟩, + fun ⟨h⟩ ↦ by simpa using eq_of_rightHomotopy _ _ h⟩ + +/-- A morphism `f` in `Arrow V` is sent to `0` in `RightFreyd V` if and only if there +exists a right homotopy between `f` and `0`. -/ +lemma quotient_map_eq_zero_iff : (quotient V).map f = 0 ↔ Nonempty (RightHomotopy f 0) := + ⟨fun h ↦ ⟨homotopyOfEq _ _ (by simpa using h)⟩, + fun ⟨h⟩ ↦ by simpa using eq_of_rightHomotopy _ _ h⟩ + +/-- If `f` is a morphism of `Arrow V` such that `f.right` is an isomorphism, then the image of `f` +in the right Freyd category is an epimorphism. -/ +lemma epi_of_isIso_right [IsIso f.right] : Epi ((quotient V).map f) where + left_cancellation g₁ g₂ eq := by + obtain ⟨g₁, rfl⟩ := (quotient V).map_surjective g₁ + obtain ⟨g₂, rfl⟩ := (quotient V).map_surjective g₂ + set h : RightHomotopy (f ≫ g₁) (f ≫ g₂) := homotopyOfEq _ _ eq + exact eq_of_rightHomotopy _ _ ⟨inv f.right ≫ h.hom, by simp [dsimp% h.comm]⟩ + +section Functor + +variable [HasZeroObject V] + +variable (V) + +open ZeroObject in +set_option backward.defeqAttrib.useBackward true in +/-- If `V` has a zero object, this is the functor from `V` to `Arrow V` +that sends an object `X` to the arrow `0 ⟶ X`. -/ +@[simps] +def rightFunctor : V ⥤ Arrow V where + obj X := Arrow.mk (0 : 0 ⟶ X) + map f := Arrow.homMk 0 f + +set_option backward.defeqAttrib.useBackward true in +instance : (rightFunctor V).Additive where + map_add {_ _ _ _} := by cat_disch + +/-- The fully faithful additive functor from `V` to `RightFreyd V` sending an object `X` of `V` +to the class of the arrow `0 ⟶ X`. -/ +abbrev functor : V ⥤ RightFreyd V := rightFunctor V ⋙ quotient V + +instance : (functor V).Additive := by dsimp [functor]; infer_instance + +set_option backward.defeqAttrib.useBackward true in +instance : (functor V).Full where + map_surjective a := by + obtain ⟨u, rfl⟩ := (quotient V).map_surjective a + exact ⟨u.right, (quotient V).congr_map (by cat_disch)⟩ + +set_option backward.defeqAttrib.useBackward true in +instance : (functor V).Faithful where + map_injective {_ _} f g eq := by + dsimp at eq + rw [quotient_map_eq_iff] at eq + simpa [← sub_eq_zero] using! eq.some.comm + +end Functor + +variable [HasBinaryBiproducts V] + +variable {u v : Arrow V} (f : u ⟶ v) + +namespace Candidate + +/-- If `f` is a morphism of `Arrow V`, this is a "candidate cokernel" of `f`, i.e. an object +in `Arrow V` whose image in `RightFreyd V` will be a cokernel of the image of `f`. -/ +abbrev cokernel := Arrow.mk (biprod.desc v.hom f.right) + +set_option backward.isDefEq.respectTransparency false in +/-- For `f : u ⟶ v` a morphism in `Arrow V`, this is the morphism `v ⟶ cokernel f` from `v` to +the "candidate cokernel" of `f`, whose image in `RightFreyd V` will be the projection to +the cokernel of the image of `f`. -/ +def π : v ⟶ cokernel f := Arrow.homMk biprod.inl (𝟙 v.right) + +set_option backward.isDefEq.respectTransparency false in +/-- The right homotopy expressing that `f ≫ π f` is sent to `0` in `RightFreyd V`. -/ +def condition : RightHomotopy (f ≫ π f) 0 where + hom := biprod.inr + comm := by simp [π] + +set_option backward.isDefEq.respectTransparency false in +instance : Epi ((quotient V).map (π f)) := + have : IsIso ((π f).right) := by simp only [π, homMk_right]; infer_instance + epi_of_isIso_right _ + +variable {w : Arrow V} (g : v ⟶ w) (h : RightHomotopy (f ≫ g) 0) + +set_option backward.isDefEq.respectTransparency false in +/-- If `f : u ⟶ v` and `g : v ⟶ w` are morphisms in `Arrow V` such that `f ≫ g` is right +homotopic to `0`, this is the morphism from the "candidate cokernel" of `f` to `w` defined +from the right homotopy. -/ +def desc : cokernel f ⟶ w := + Arrow.homMk (biprod.desc g.left h.hom) g.right (biprod.hom_ext' _ _ (by simp) + (by simp [← h.comm])) + +set_option backward.isDefEq.respectTransparency false in +@[reassoc (attr := simp)] +lemma π_desc : π f ≫ desc f g h = g := by ext <;> simp [π, desc] + +/-- For `f` a morphism in `Arrow V`, this is a cokernel cofork of `(quotient V).map f`. -/ +def cokernelCofork : CokernelCofork ((quotient V).map f) := + CokernelCofork.ofπ ((quotient V).map (Candidate.π f)) + (eq_of_rightHomotopy _ _ (Candidate.condition f)) + +set_option backward.isDefEq.respectTransparency false in +/-- For `f` a morphism in `Arrow V`, the cokernel cofork of `(quotient V).map f` constructed +in `cokernelCofork` is a colimit cofork. -/ +def isColimitCokernelCofork : IsColimit (cokernelCofork f) := + CokernelCofork.IsColimit.ofπ' _ + (eq_of_rightHomotopy _ _ (Candidate.condition f)) + (fun g hg ↦ Nonempty.some (by + obtain ⟨g, rfl⟩ := (quotient V).map_surjective g + exact ⟨(quotient V).map (desc f g (homotopyOfEq _ _ hg)), + by simp [← Functor.map_comp]⟩)) + +end Candidate + +/-- The category `RightFreyd V` has all cokernels if `V` has binary biproducts. -/ +instance : HasCokernels (RightFreyd V) where + has_colimit f := ⟨by + obtain ⟨f, rfl⟩ := (quotient V).map_surjective f + exact ⟨_, Candidate.isColimitCokernelCofork f⟩⟩ + +end RightFreyd + +end CategoryTheory.Preadditive diff --git a/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean b/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean new file mode 100644 index 00000000000000..d8955a1baf21ae --- /dev/null +++ b/Mathlib/CategoryTheory/Preadditive/Injective/InjectiveObject.lean @@ -0,0 +1,63 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.CategoryTheory.Limits.FullSubcategory +public import Mathlib.CategoryTheory.Preadditive.Biproducts +public import Mathlib.CategoryTheory.Preadditive.Injective.Basic + +/-! +# The full subcategory of injective objects + +-/ + +@[expose] public section + +universe v u + +namespace CategoryTheory + +open Limits ZeroObject + +variable (C : Type u) [Category.{v} C] + +/-- The full subcategory of injective objects in a category `C`. -/ +abbrev InjectiveObject : Type u := ObjectProperty.FullSubcategory (isInjective C) + +namespace InjectiveObject + +instance (J : Type*) : + ObjectProperty.IsClosedUnderLimitsOfShape (isInjective C) (Discrete J) where + limitsOfShape_le := by + rintro Y ⟨p⟩ + have (j : J) : Injective (p.diag.obj ⟨j⟩) := p.prop_diag_obj _ + exact ⟨fun q i _ ↦ ⟨p.isLimit.lift (Cone.mk _ + (Discrete.natTrans (fun ⟨j⟩ ↦ (Injective.factorThru (q ≫ p.π.app ⟨j⟩) i :)))), + p.isLimit.hom_ext (fun ⟨j⟩ ↦ by simp [p.isLimit.fac])⟩⟩ + +instance [HasFiniteProducts C] : HasFiniteProducts (InjectiveObject C) where + out _ := inferInstance + +instance [Preadditive C] [HasFiniteProducts C] : HasFiniteBiproducts (InjectiveObject C) := + HasFiniteBiproducts.of_hasFiniteProducts + +instance [Preadditive C] [HasBinaryBiproducts C] : HasBinaryBiproducts (InjectiveObject C) := + HasBinaryBiproducts.of_hasBinaryProducts + +instance [HasZeroMorphisms C] [HasZeroObject C] : (isInjective C).ContainsZero where + exists_zero := ⟨0, by simp [IsZero.iff_id_eq_zero], Injective.zero_injective⟩ + +/-- The inclusion `InjectiveObject C ⥤ C` of the full subcategory of +injective objects in `C`. -/ +abbrev ι : InjectiveObject C ⥤ C := ObjectProperty.ι _ + +instance (X : InjectiveObject C) : Injective ((ι C).obj X) := X.2 + +instance (X : InjectiveObject C) : Injective X.obj := X.2 + +end InjectiveObject + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Preadditive/LeftExact.lean b/Mathlib/CategoryTheory/Preadditive/LeftExact.lean index 7eef13254cee94..def108ecf37a58 100644 --- a/Mathlib/CategoryTheory/Preadditive/LeftExact.lean +++ b/Mathlib/CategoryTheory/Preadditive/LeftExact.lean @@ -17,9 +17,9 @@ preserves kernels. The dual result holds for right exact functors and cokernels. ## Main results -* We first derive preservation of binary product in the lemma - `preservesBinaryProductsOfPreservesKernels`, -* then show the preservation of equalizers in `preservesEqualizerOfPreservesKernels`, +* We first derive preservation of binary products in the lemma + `preservesBinaryProducts_of_preservesKernels`, +* then show the preservation of equalizers in `preservesEqualizer_of_preservesKernels`, * and then derive the preservation of all finite limits with the usual construction. -/ @@ -187,8 +187,8 @@ lemma preservesCoequalizer_of_preservesCokernels (isColimitCoforkOfCokernelCofork ((IsColimit.precomposeHomEquiv p.symm _).symm iFc)) (Cofork.ext (Iso.refl _) (by simp [p])) -/-- A functor between preadditive categories preserves all coequalizers if it preserves all kernels. --/ +/-- A functor between preadditive categories preserves all coequalizers if it preserves all +cokernels. -/ lemma preservesCoequalizers_of_preservesCokernels [∀ {X Y} (f : X ⟶ Y), PreservesColimit (parallelPair f 0) F] : PreservesColimitsOfShape WalkingParallelPair F where @@ -197,8 +197,8 @@ lemma preservesCoequalizers_of_preservesCokernels (K.map Limits.WalkingParallelPairHom.right) apply preservesColimit_of_iso_diagram F (diagramIsoParallelPair K).symm -/-- A functor between preadditive categories which preserves kernels preserves all finite limits. --/ +/-- A functor between preadditive categories which preserves cokernels preserves all finite +colimits. -/ lemma preservesFiniteColimits_of_preservesCokernels [HasFiniteCoproducts C] [HasCoequalizers C] [HasZeroObject C] [HasZeroObject D] [∀ {X Y} (f : X ⟶ Y), PreservesColimit (parallelPair f 0) F] : PreservesFiniteColimits F := by diff --git a/Mathlib/CategoryTheory/Preadditive/Schur.lean b/Mathlib/CategoryTheory/Preadditive/Schur.lean index 254cf45acf2c65..4b32d29f7232e4 100644 --- a/Mathlib/CategoryTheory/Preadditive/Schur.lean +++ b/Mathlib/CategoryTheory/Preadditive/Schur.lean @@ -13,6 +13,7 @@ public import Mathlib.FieldTheory.IsAlgClosed.Spectrum /-! # Schur's lemma + We first prove the part of Schur's Lemma that holds in any preadditive category with kernels, that any nonzero morphism between simple objects is an isomorphism. diff --git a/Mathlib/CategoryTheory/Presentable/Basic.lean b/Mathlib/CategoryTheory/Presentable/Basic.lean index 36fd4d00df525e..f1d14fbe781e7c 100644 --- a/Mathlib/CategoryTheory/Presentable/Basic.lean +++ b/Mathlib/CategoryTheory/Presentable/Basic.lean @@ -237,35 +237,80 @@ lemma isCardinalPresentable_iff_of_isEquivalence · intro infer_instance +section + +variable {J : Type*} [Category* J] {D : J ⥤ C} + +lemma Limits.exists_hom_of_preservesColimit_coyoneda {c : Cocone D} (hc : IsColimit c) {X : C} + [PreservesColimit D (coyoneda.obj (.op X))] (f : X ⟶ c.pt) : + ∃ (j : J) (p : X ⟶ D.obj j), p ≫ c.ι.app j = f := + Types.jointly_surjective_of_isColimit (isColimitOfPreserves (coyoneda.obj (.op X)) hc) f + +lemma Limits.exists_eq_of_preservesColimit_coyoneda [IsFiltered J] {c : Cocone D} + (hc : IsColimit c) {X : C} [PreservesColimit D (coyoneda.obj (.op X))] + {i j : J} (f : X ⟶ D.obj i) (g : X ⟶ D.obj j) (h : f ≫ c.ι.app i = g ≫ c.ι.app j) : + ∃ (k : J) (u : i ⟶ k) (v : j ⟶ k), f ≫ D.map u = g ≫ D.map v := + (Types.FilteredColimit.isColimit_eq_iff _ (isColimitOfPreserves (coyoneda.obj (.op X)) hc)).mp h + +lemma Limits.exists_eq_of_preservesColimit_coyoneda_self [IsFiltered J] {c : Cocone D} + (hc : IsColimit c) {X : C} [PreservesColimit D (coyoneda.obj (.op X))] + {i : J} (f g : X ⟶ D.obj i) (h : f ≫ c.ι.app i = g ≫ c.ι.app i) : + ∃ (j : J) (a : i ⟶ j), f ≫ D.map a = g ≫ D.map a := + (Types.FilteredColimit.isColimit_eq_iff' + (isColimitOfPreserves (coyoneda.obj (.op X)) hc) f g).mp h + +lemma Limits.exists_hom_of_preservesColimit_yoneda {c : Cone D} (hc : IsLimit c) {X : C} + [PreservesColimit D.op (yoneda.obj X)] (f : c.pt ⟶ X) : + ∃ (j : J) (p : D.obj j ⟶ X), c.π.app j ≫ p = f := by + obtain ⟨j, p, hp⟩ := Types.jointly_surjective_of_isColimit + (isColimitOfPreserves (yoneda.obj X) hc.op) f + exact ⟨j.unop, p, hp⟩ + +lemma Limits.exists_eq_of_preservesColimit_yoneda [IsCofiltered J] {c : Cone D} (hc : IsLimit c) + {X : C} [PreservesColimit D.op (yoneda.obj X)] + {i j : J} (f : D.obj i ⟶ X) (g : D.obj j ⟶ X) (h : c.π.app i ≫ f = c.π.app j ≫ g) : + ∃ (k : J) (u : k ⟶ i) (v : k ⟶ j), D.map u ≫ f = D.map v ≫ g := by + obtain ⟨k, u, v, huv⟩ := + (Types.FilteredColimit.isColimit_eq_iff _ (isColimitOfPreserves (yoneda.obj X) hc.op)).mp h + exact ⟨k.unop, u.unop, v.unop, huv⟩ + +lemma Limits.exists_eq_of_preservesColimit_yoneda_self [IsCofiltered J] {c : Cone D} + (hc : IsLimit c) {X : C} [PreservesColimit D.op (yoneda.obj X)] + {i : J} (f g : D.obj i ⟶ X) (h : c.π.app i ≫ f = c.π.app i ≫ g) : + ∃ (j : J) (a : j ⟶ i), D.map a ≫ f = D.map a ≫ g := by + obtain ⟨j, a, ha⟩ := (Types.FilteredColimit.isColimit_eq_iff' + (isColimitOfPreserves (yoneda.obj X) hc.op) f g).mp h + exact ⟨j.unop, a.unop, ha⟩ + variable {X} in lemma IsCardinalPresentable.exists_hom_of_isColimit [IsCardinalPresentable X κ] - {J : Type u₂} [Category.{v₂} J] [EssentiallySmall.{w} J] [IsCardinalFiltered J κ] + [EssentiallySmall.{w} J] [IsCardinalFiltered J κ] {F : J ⥤ C} {c : Cocone F} (hc : IsColimit c) (f : X ⟶ c.pt) : ∃ (j : J) (f' : X ⟶ F.obj j), f' ≫ c.ι.app j = f := by have := preservesColimitsOfShape_of_isCardinalPresentable_of_essentiallySmall X κ J - exact Types.jointly_surjective_of_isColimit (isColimitOfPreserves (coyoneda.obj (op X)) hc) f + exact exists_hom_of_preservesColimit_coyoneda hc f variable {X} in lemma IsCardinalPresentable.exists_eq_of_isColimit [IsCardinalPresentable X κ] - {J : Type u₂} [Category.{v₂} J] [EssentiallySmall.{w} J] [IsCardinalFiltered J κ] + [EssentiallySmall.{w} J] [IsCardinalFiltered J κ] {F : J ⥤ C} {c : Cocone F} (hc : IsColimit c) {i₁ i₂ : J} (f₁ : X ⟶ F.obj i₁) (f₂ : X ⟶ F.obj i₂) (hf : f₁ ≫ c.ι.app i₁ = f₂ ≫ c.ι.app i₂) : ∃ (j : J) (u : i₁ ⟶ j) (v : i₂ ⟶ j), f₁ ≫ F.map u = f₂ ≫ F.map v := by have := preservesColimitsOfShape_of_isCardinalPresentable_of_essentiallySmall X κ J have := isFiltered_of_isCardinalFiltered J κ - exact (Types.FilteredColimit.isColimit_eq_iff _ - (isColimitOfPreserves (coyoneda.obj (op X)) hc)).1 hf + exact exists_eq_of_preservesColimit_coyoneda hc f₁ f₂ hf variable {X} in lemma IsCardinalPresentable.exists_eq_of_isColimit' [IsCardinalPresentable X κ] - {J : Type u₂} [Category.{v₂} J] [EssentiallySmall.{w} J] [IsCardinalFiltered J κ] + [EssentiallySmall.{w} J] [IsCardinalFiltered J κ] {F : J ⥤ C} {c : Cocone F} (hc : IsColimit c) {i : J} (f₁ f₂ : X ⟶ F.obj i) (hf : f₁ ≫ c.ι.app i = f₂ ≫ c.ι.app i) : ∃ (j : J) (u : i ⟶ j), f₁ ≫ F.map u = f₂ ≫ F.map u := by have := preservesColimitsOfShape_of_isCardinalPresentable_of_essentiallySmall X κ J have := isFiltered_of_isCardinalFiltered J κ - exact (Types.FilteredColimit.isColimit_eq_iff' - (isColimitOfPreserves (coyoneda.obj (op X)) hc) f₁ f₂).1 hf + exact exists_eq_of_preservesColimit_coyoneda_self hc f₁ f₂ hf + +end lemma isCardinalPresentable_iff_isCardinalAccessible_uliftCoyoneda_obj : IsCardinalPresentable X κ ↔ (uliftCoyoneda.{t}.obj (op X)).IsCardinalAccessible κ := by diff --git a/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean b/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean index 8217cf1f7859a6..b662c5e2dd2dd2 100644 --- a/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean +++ b/Mathlib/CategoryTheory/Presentable/CardinalDirectedPoset.lean @@ -13,12 +13,16 @@ public import Mathlib.Order.Category.PartOrdEmb # The κ-accessible category of κ-directed posets Given a regular cardinal `κ : Cardinal.{u}`, we define the -category `CardinalFilteredPoset κ` of `κ`-directed partially ordered +category `CardinalDirectedPoset κ` of `κ`-directed partially ordered types (with order embeddings as morphisms), and we show that it is a `κ`-accessible category. +The notion of `κ`-directed partially ordered type is implemented +using the categorial notion `IsCardinalFiltered`: we may consider +"`κ`-directed" and "`κ`-filtered" as synonyms. + If `κ ≤ κ'` where `κ'` is also a regular cardinal, we characterize -the `κ'`-presentable objects of `CardinalFilteredPoset κ` as +the `κ'`-presentable objects of `CardinalDirectedPoset κ` as the objects `J` such that the underlying type `J.obj` has cardinality `< κ'`. @@ -38,7 +42,10 @@ namespace PartOrdEmb variable (κ : Cardinal.{u}) [Fact κ.IsRegular] /-- The property of objects in `PartOrdEmb` that are -satisfied by partially ordered types of cardinality `< κ`. -/ +satisfied by `κ`-directed partially ordered types. +(Note: for partially ordered types, "`κ`-directed" and +"`κ`-filtered" are synonyms. This is implemented using the +categorical notion `IsCardinalFiltered`.) -/ abbrev isCardinalFiltered : ObjectProperty PartOrdEmb.{u} := fun X ↦ IsCardinalFiltered X κ @@ -92,77 +99,77 @@ variable (κ : Cardinal.{u}) [Fact κ.IsRegular] /-- The category of `κ`-filtered partially ordered types, with morphisms given by order embeddings. -/ -abbrev CardinalFilteredPoset := +abbrev CardinalDirectedPoset := (PartOrdEmb.isCardinalFiltered κ).FullSubcategory variable {κ} -/-- The embedding of the category of `κ`-filtered +/-- The embedding of the category of `κ`-directed partially ordered types in the category of partially ordered types. -/ -abbrev CardinalFilteredPoset.ι : CardinalFilteredPoset κ ⥤ PartOrdEmb := +abbrev CardinalDirectedPoset.ι : CardinalDirectedPoset κ ⥤ PartOrdEmb := ObjectProperty.ι _ -namespace CardinalFilteredPoset +namespace CardinalDirectedPoset /-- Constructor for objects in `CardinalFilteredPoset κ`. -/ -abbrev of (J : PartOrdEmb.{u}) [IsCardinalFiltered J κ] : CardinalFilteredPoset κ where +abbrev of (J : PartOrdEmb.{u}) [IsCardinalFiltered J κ] : CardinalDirectedPoset κ where obj := J property := inferInstance -lemma Hom.injective {J₁ J₂ : CardinalFilteredPoset κ} (f : J₁ ⟶ J₂) : +lemma Hom.injective {J₁ J₂ : CardinalDirectedPoset κ} (f : J₁ ⟶ J₂) : Function.Injective f := f.hom.injective -lemma Hom.le_iff_le {J₁ J₂ : CardinalFilteredPoset κ} (f : J₁ ⟶ J₂) (x₁ x₂ : J₁.obj) : +lemma Hom.le_iff_le {J₁ J₂ : CardinalDirectedPoset κ} (f : J₁ ⟶ J₂) (x₁ x₂ : J₁.obj) : f x₁ ≤ f x₂ ↔ x₁ ≤ x₂ := f.hom.hom.le_iff_le -instance (J : CardinalFilteredPoset κ) : IsCardinalFiltered J.obj κ := J.property +instance (J : CardinalDirectedPoset κ) : IsCardinalFiltered J.obj κ := J.property -instance (J : CardinalFilteredPoset κ) : IsFiltered J.obj := +instance (J : CardinalDirectedPoset κ) : IsFiltered J.obj := isFiltered_of_isCardinalFiltered _ κ -instance (J : CardinalFilteredPoset κ) : Nonempty J.obj := IsFiltered.nonempty +instance (J : CardinalDirectedPoset κ) : Nonempty J.obj := IsFiltered.nonempty -instance : HasCardinalFilteredColimits (CardinalFilteredPoset κ) κ where +instance : HasCardinalFilteredColimits (CardinalDirectedPoset κ) κ where hasColimitsOfShape J _ _ := by have := isFiltered_of_isCardinalFiltered J κ infer_instance instance (A : Type u) [SmallCategory A] [IsCardinalFiltered A κ] : - PreservesColimitsOfShape A (forget (CardinalFilteredPoset κ)) := by + PreservesColimitsOfShape A (forget (CardinalDirectedPoset κ)) := by have := isFiltered_of_isCardinalFiltered A κ - change PreservesColimitsOfShape A (CardinalFilteredPoset.ι ⋙ forget _) + change PreservesColimitsOfShape A (CardinalDirectedPoset.ι ⋙ forget _) infer_instance -instance (J : CardinalFilteredPoset κ) (κ' : Cardinal.{u}) [Fact κ'.IsRegular] : +instance (J : CardinalDirectedPoset κ) (κ' : Cardinal.{u}) [Fact κ'.IsRegular] : IsCardinalFiltered (WithTop (J.obj)) κ' := isCardinalFiltered_of_hasTerminal _ _ -/-- The map `CardinalFilteredPoset κ → CardinalFilteredPoset κ` which sends +/-- The map `CardinalDirectedPoset κ → CardinalDirectedPoset κ` which sends a partially ordered `κ`-filtered type `J` to `WithTop J`. -/ -abbrev withTop (J : CardinalFilteredPoset κ) : CardinalFilteredPoset κ := +abbrev withTop (J : CardinalDirectedPoset κ) : CardinalDirectedPoset κ := .of (.of (WithTop J.obj)) section -variable {J : CardinalFilteredPoset κ} (P : Set J.obj → Prop) +variable {J : CardinalDirectedPoset κ} (P : Set J.obj → Prop) [IsDirectedOrder (Subtype P)] [Nonempty (Subtype P)] [∀ (S : Subtype P), IsCardinalFiltered S.val κ] set_option backward.defeqAttrib.useBackward true in /-- Given a predicate `P : Set J.obj → Prop` on the underlying type -of `J : CardinalFilteredPoset κ` such that all the subsets satisfying `P` -are `κ`-filtered, this is the functor `Subtype P ⥤ CardinalFilteredPoset κ` +of `J : CardinalDirectedPoset κ` such that all the subsets satisfying `P` +are `κ`-filtered, this is the functor `Subtype P ⥤ CardinalDirectedPoset κ` which sends a subset `S` of `J` satisfying `P` to the induced -partially ordered type `J`, as an object in `CardinalFilteredPoset κ`. -/ +partially ordered type `J`, as an object in `CardinalDirectedPoset κ`. -/ @[simps!] -def functorOfPredicateSet : Subtype P ⥤ CardinalFilteredPoset κ := +def functorOfPredicateSet : Subtype P ⥤ CardinalDirectedPoset κ := ObjectProperty.lift _ (PartOrdEmb.functorOfPredicateSet P) (fun S ↦ by dsimp; infer_instance) /-- Given a predicate `P : Set J.obj → Prop` on the underlying type -of `J : CardinalFilteredPoset κ` such that all the subsets satisfying `P` +of `J : CardinalDirectedPoset κ` such that all the subsets satisfying `P` are `κ`-filtered, this is the cocone with point `J` given by all the inclusions of the subsets satisfying `P`. -/ @[simps] @@ -170,22 +177,22 @@ def coconeOfPredicateSet : Cocone (functorOfPredicateSet P) where pt := J ι.app j := ObjectProperty.homMk ((PartOrdEmb.coconeOfPredicateSet P).ι.app j) -/-- Let `P` be a predicate on `Set J.obj` where `J : CardinalFilteredPoset κ`. +/-- Let `P` be a predicate on `Set J.obj` where `J : CardinalDirectedPoset κ`. We assume that `Subtype P` is directed and nonempty, and that any `a : J.obj` belongs to some `S : Set J.obj` satisfying `P`. Then, `J` is the colimit in the -category `CardinalFilteredPoset κ` of these subsets. -/ +category `CardinalDirectedPoset κ` of these subsets. -/ noncomputable def isColimitCoconeOfPredicateSet (hP : ∀ (a : J.obj), ∃ (S : Set J.obj), P S ∧ a ∈ S) : IsColimit (coconeOfPredicateSet P) := - isColimitOfReflects CardinalFilteredPoset.ι + isColimitOfReflects CardinalDirectedPoset.ι (PartOrdEmb.isColimitOfPredicateSet P hP) end variable (κ) in -/-- The property of posets in `CardinalFilteredPoset κ` that are +/-- The property of posets in `CardinalDirectedPoset κ` that are of cardinality `< κ` and have terminal object. -/ -def hasCardinalLTWithTerminal : ObjectProperty (CardinalFilteredPoset κ) := +def hasCardinalLTWithTerminal : ObjectProperty (CardinalDirectedPoset κ) := fun J ↦ HasCardinalLT J.obj κ ∧ HasTerminal J.obj instance : ObjectProperty.EssentiallySmall.{u} (hasCardinalLTWithTerminal κ) where @@ -194,7 +201,7 @@ instance : ObjectProperty.EssentiallySmall.{u} (hasCardinalLTWithTerminal κ) wh let α : Type u := Σ (S : Set X) (_ : PartialOrder S), ULift.{u} (PLift (IsCardinalFiltered S κ)) let (a : α) : PartialOrder a.1 := a.2.1 - let ι (a : α) : CardinalFilteredPoset κ := + let ι (a : α) : CardinalDirectedPoset κ := { obj := .of a.1 property := a.2.2.down.down } refine ⟨.ofObj ι, inferInstance, fun J ⟨hJ, _⟩ ↦ ?_⟩ @@ -205,11 +212,11 @@ instance : ObjectProperty.EssentiallySmall.{u} (hasCardinalLTWithTerminal κ) wh let e' : Set.range f ≃o J.obj := { toEquiv := e.symm, map_rel_iff' := by rfl } exact ⟨_, ⟨⟨Set.range f, inferInstance, ⟨⟨IsCardinalFiltered.of_equivalence κ e'.symm.equivalence⟩⟩⟩⟩, - ⟨CardinalFilteredPoset.ι.preimageIso (PartOrdEmb.Iso.mk (by exact e'.symm))⟩⟩ + ⟨CardinalDirectedPoset.ι.preimageIso (PartOrdEmb.Iso.mk (by exact e'.symm))⟩⟩ set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in -lemma isCardinalPresentable_of_hasCardinalLT_of_le (J : CardinalFilteredPoset κ) +lemma isCardinalPresentable_of_hasCardinalLT_of_le (J : CardinalDirectedPoset κ) {κ' : Cardinal.{u}} [Fact κ'.IsRegular] (hJ : HasCardinalLT J.obj κ') (h : κ ≤ κ') : IsCardinalPresentable J κ' where preservesColimitOfShape A _ _ := ⟨fun {F} ↦ ⟨fun {c} hc ↦ ⟨by @@ -255,7 +262,7 @@ lemma isCardinalPresentable_of_hasCardinalLT_of_le (J : CardinalFilteredPoset κ section -variable (J : CardinalFilteredPoset κ) +variable (J : CardinalDirectedPoset κ) -- `@[nolint unusedArguments]` allows to setup some instances which uses -- the fact that `κ'` is regular. @@ -314,7 +321,7 @@ colimit of its subsets that are of cardinality `< κ'` and contain `⊤`. -/ abbrev coconeWithTop : Cocone (functorOfPredicateSet (J.PropSetWithTop κ')) := coconeOfPredicateSet (PropSetWithTop J κ') -/-- If `J : CardinalFilteredPoset κ` and `κ'` is any regular cardinal, +/-- If `J : CardinalDirectedPoset κ` and `κ'` is any regular cardinal, then `J.withTop` is the `κ'`-filtered colimit of its subsets that are of cardinality `< κ'` and contain `⊤`. -/ noncomputable def isColimitCoconeWithTop : IsColimit (coconeWithTop J κ') := @@ -341,15 +348,15 @@ protected lemma isCardinalPresentable_iff (h : κ ≤ κ') : end -protected lemma isCardinalPresentable_iff' (J : CardinalFilteredPoset κ) : +protected lemma isCardinalPresentable_iff' (J : CardinalDirectedPoset κ) : IsCardinalPresentable J κ ↔ HasCardinalLT J.obj κ := - CardinalFilteredPoset.isCardinalPresentable_iff _ (le_refl _) + CardinalDirectedPoset.isCardinalPresentable_iff _ (le_refl _) section -variable (J : CardinalFilteredPoset κ) +variable (J : CardinalDirectedPoset κ) -/-- Given `J : CardinalFilteredPoset κ`, this is the predicate +/-- Given `J : CardinalDirectedPoset κ`, this is the predicate on `Set J.obj` that is satisfied by subsets that are of cardinality `< κ` and have a terminal object. -/ def PropSet (S : Set J.obj) : Prop := @@ -397,15 +404,15 @@ instance : IsDirectedOrder (Subtype J.PropSet) := instance : Nonempty (Subtype J.PropSet) := IsFiltered.nonempty -/-- For any object `J : CardinalFilteredPoset κ`, this is a colimit +/-- For any object `J : CardinalDirectedPoset κ`, this is a colimit cocone exhibiting `J` as the colimit of its subsets that are of cardinality `< κ` and have a terminal object. -/ abbrev cocone : Cocone (functorOfPredicateSet J.PropSet) := coconeOfPredicateSet J.PropSet -/-- Any object `J : CardinalFilteredPoset κ` is a colimit +/-- Any object `J : CardinalDirectedPoset κ` is a colimit of its subsets that are of cardinality `< κ` and have a terminal object. -/ -noncomputable def isColimitCocone (J : CardinalFilteredPoset κ) : +noncomputable def isColimitCocone (J : CardinalDirectedPoset κ) : IsColimit (cocone J) := isColimitCoconeOfPredicateSet _ (fun a ↦ ⟨_, propSet_singleton a, by simp⟩) @@ -424,11 +431,79 @@ lemma isCardinalFilteredGenerator_hasCardinalLTWithTerminal : isColimit := isColimitCocone J prop_diag_obj j := j.prop }⟩⟩ -instance : IsCardinalAccessibleCategory (CardinalFilteredPoset κ) κ where +instance : IsCardinalAccessibleCategory (CardinalDirectedPoset κ) κ where exists_generator := ⟨hasCardinalLTWithTerminal κ, inferInstance, isCardinalFilteredGenerator_hasCardinalLTWithTerminal κ⟩ +variable (κ) (X : Type u) + +/-- Given a cardinal `κ` and a type `X`, this is the subtype of `Set X` +consisting of subsets of `X` of cardinality `< κ`. -/ +abbrev SetCardinalLT := Subtype (fun (S : Set X) ↦ HasCardinalLT S κ) + +variable {X} in +/-- Given a regular cardinal `κ` and `x : X`, this is the singleton `{x}`, +considered as a subset of `X` of cardinality `< κ`. -/ +abbrev SetCardinalLT.singleton (x : X) : SetCardinalLT κ X := + ⟨{x}, hasCardinalLT_of_finite _ _ (Cardinal.IsRegular.aleph0_le Fact.out)⟩ + +instance : IsCardinalFiltered (SetCardinalLT κ X) κ := + isCardinalFiltered_preorder _ _ + (fun K f hK ↦ + ⟨⟨⋃ (k : K), (f k).val, hasCardinalLT_iUnion _ + (by rwa [hasCardinalLT_iff_cardinal_mk_lt]) (fun k ↦ (f k).prop)⟩, + Set.subset_iUnion (fun k ↦ (f k).val)⟩) + +/-- Given a regular cardinal `κ` and a type `X`, this is the `κ`-filtered +partially ordered type of subsets of `X` of cardinality `< κ`, +as an object of the category `CardinalDirectedPoset κ`. -/ +abbrev setCardinalLT : CardinalDirectedPoset κ := + .of (PartOrdEmb.of (SetCardinalLT κ X)) + +end CardinalDirectedPoset + +@[deprecated (since := "2026-06-24")] alias CardinalFilteredPoset := + CardinalDirectedPoset + +namespace CardinalFilteredPoset + +@[deprecated (since := "2026-06-24")] alias ι := CardinalDirectedPoset.ι +@[deprecated (since := "2026-06-24")] alias of := CardinalDirectedPoset.of +@[deprecated (since := "2026-06-24")] alias Hom.injective := CardinalDirectedPoset.Hom.injective +@[deprecated (since := "2026-06-24")] alias Hom.le_iff_le := CardinalDirectedPoset.Hom.le_iff_le +@[deprecated (since := "2026-06-24")] alias withTop := CardinalDirectedPoset.withTop +@[deprecated (since := "2026-06-24")] +alias functorOfPredicateSet := CardinalDirectedPoset.functorOfPredicateSet +@[deprecated (since := "2026-06-24")] +alias coconeOfPredicateSet := CardinalDirectedPoset.coconeOfPredicateSet +@[deprecated (since := "2026-06-24")] +alias isColimitCoconeOfPredicateSet := CardinalDirectedPoset.isColimitCoconeOfPredicateSet +@[deprecated (since := "2026-06-24")] +alias hasCardinalLTWithTerminal := CardinalDirectedPoset.hasCardinalLTWithTerminal +@[deprecated (since := "2026-06-24")] +alias isCardinalPresentable_of_hasCardinalLT_of_le := + CardinalDirectedPoset.isCardinalPresentable_of_hasCardinalLT_of_le +@[deprecated (since := "2026-06-24")] +alias PropSetWithTop := CardinalDirectedPoset.PropSetWithTop +@[deprecated (since := "2026-06-24")] +alias propSetWithTop_pair := CardinalDirectedPoset.propSetWithTop_pair +@[deprecated (since := "2026-06-24")] +alias exists_mem_propSetWithTop := CardinalDirectedPoset.exists_mem_propSetWithTop +@[deprecated (since := "2026-06-24")] +alias coconeWithTop := CardinalDirectedPoset.coconeWithTop +@[deprecated (since := "2026-06-24")] +alias isColimitCoconeWithTop := CardinalDirectedPoset.isColimitCoconeWithTop +@[deprecated (since := "2026-06-24")] +alias isCardinalPresentable_iff := CardinalDirectedPoset.isCardinalPresentable_iff +@[deprecated (since := "2026-06-24")] +alias isCardinalPresentable_iff' := CardinalDirectedPoset.isCardinalPresentable_iff' +@[deprecated (since := "2026-06-24")] alias PropSet := CardinalDirectedPoset.PropSet +@[deprecated (since := "2026-06-24")] +alias propSet_singleton := CardinalDirectedPoset.propSet_singleton +@[deprecated (since := "2026-06-24")] alias cocone := CardinalDirectedPoset.cocone +@[deprecated (since := "2026-06-24")] alias isColimitCocone := CardinalDirectedPoset.isColimitCocone + end CardinalFilteredPoset end CategoryTheory diff --git a/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean b/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean index 3ff5e8e417ea26..cb0d056b5e23e0 100644 --- a/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean +++ b/Mathlib/CategoryTheory/Presentable/ColimitPresentation.lean @@ -151,7 +151,7 @@ def bind {X : C} (P : ColimitPresentation J X) (Q : ∀ j, ColimitPresentation ( isColimit.fac := fun c ⟨j, i⟩ ↦ by simp [P.isColimit.fac, (Q j).isColimit.fac] isColimit.uniq c m hm := by refine P.isColimit.hom_ext fun j ↦ ?_ - simp only [Functor.const_obj_obj, P.isColimit.fac] + simp only [P.isColimit.fac] refine (Q j).isColimit.hom_ext fun i ↦ ?_ simpa [(Q j).isColimit.fac] using hm (.mk _ j i) diff --git a/Mathlib/CategoryTheory/Presentable/SharplyLT/Basic.lean b/Mathlib/CategoryTheory/Presentable/SharplyLT/Basic.lean new file mode 100644 index 00000000000000..1fe915d41331d6 --- /dev/null +++ b/Mathlib/CategoryTheory/Presentable/SharplyLT/Basic.lean @@ -0,0 +1,265 @@ +/- +Copyright (c) 2026 Joël Riou. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Joël Riou +-/ +module + +public import Mathlib.CategoryTheory.Presentable.CardinalDirectedPoset +public import Mathlib.CategoryTheory.Presentable.Dense +public import Mathlib.Order.TransfiniteIteration + +/-! +# Sharply smaller regular cardinals + +In this file, we introduce the predicate `Cardinal.SharplyLT`. Given two regular +cardinals `κ₁ < κ₂`, this condition can be described in different ways: +(i) the category `CardinalDirectedPoset κ₁` (of `κ₁`-directed partially ordered + types, with order embeddings as morphisms), is `κ₂`-accessible; +(ii) any `κ₁`-accessible category is `κ₂`-accessible. +(iii) for any type `X` of cardinality `< κ₂`, there exists a cofinal set of + cardinality `< κ₂` in the subtype of subsets of `X` of cardinality `< κ₁`; +(iv) for any `κ₁`-directed partially ordered type `X` and any subset `A` of `X` + of cardinality `< κ₂`, there exists a `κ₁`-directed subset `B` of `X` containing `A` + that is of cardinality `< κ₂`. +The equivalence of these conditions (i)-(iv) is Theorem 2.11 in the book by Adámek and Rosický +((i) → (iii) is `exists_cofinal_of_isCardinalAccessibleCategory_cardinalDirectedPoset`, +(iii) → (iv) is `exists_isCardinalFiltered_set_of_exists_cofinal`, (ii) → (i) is obvious; +the rest is TODO @joelriou). Here, we take (i) as the definition. + +## References +* [Adámek, J. and Rosický, J., *Locally presentable and accessible categories*][Adamek_Rosicky_1994] + +-/ + +universe w v u + +open CategoryTheory Limits + +namespace Cardinal + +variable {κ₁ κ₂ : Cardinal.{w}} [Fact κ₁.IsRegular] [Fact κ₂.IsRegular] + +variable (κ₁ κ₂) in +/-- If `κ₁ < κ₂` are two regular cardinals, we say that `κ₁` is sharply +smaller than `κ₂` if the category `CardinalDirectedPoset κ₁` +is `κ₂`-accessible. There are other characterizations (TODO @joelriou), +including the property that any `κ₁`-accessible category is +also `κ₂`-accessible. -/ +public structure SharplyLT : Prop where + lt : κ₁ < κ₂ + isCardinalAccessible_cardinalDirectedPoset : + IsCardinalAccessibleCategory (CardinalDirectedPoset κ₁) κ₂ + +namespace SharplyLT + +public lemma le (h : SharplyLT κ₁ κ₂) : κ₁ ≤ κ₂ := h.lt.le + +set_option backward.defeqAttrib.useBackward true in +open CardinalDirectedPoset in +/-- This is the implication (i) → (iii) in the characterizations +of `SharplyLT κ₁ κ₂` in the docstring of this file. -/ +public lemma exists_cofinal_of_isCardinalAccessibleCategory_cardinalDirectedPoset + (h : κ₁ ≤ κ₂) [IsCardinalAccessibleCategory (CardinalDirectedPoset κ₁) κ₂] + {X : Type w} (hX : HasCardinalLT X κ₂) : + ∃ (Y : Set (SetCardinalLT κ₁ X)), HasCardinalLT Y κ₂ ∧ IsCofinal Y := by + -- We write the partially ordered type `SetCardinalLT κ₁ X` of subsets + -- of `X` of cardinality `< κ₁` as a `κ₂`-filtered colimit (with index + -- category `J`) of `κ₂`-presentable objects (i.e. partially ordered + -- types of cardinality `< κ₂`.) + obtain ⟨J, _, _, ⟨p⟩⟩ := (isCardinalFilteredGenerator_isCardinalPresentable + (CardinalDirectedPoset κ₁) κ₂).exists_colimitsOfShape (setCardinalLT κ₁ X) + have : IsCardinalFiltered J κ₁ := .of_le _ h + have hp (j : J) : HasCardinalLT (p.diag.obj j).obj κ₂ := by + rw [← CardinalDirectedPoset.isCardinalPresentable_iff _ h] + exact p.prop_diag_obj j + -- For each `x : X`, we choose `j : J` in such a way that the singleton + -- `{x}` belongs to the image of `p.diag.obj j` in `SetCardinalLT κ₁ X`. + choose j y hy using fun x ↦ Types.jointly_surjective_of_isColimit + (isColimitOfPreserves (forget (CardinalDirectedPoset κ₁)) p.isColimit) + (SetCardinalLT.singleton κ₁ x) + dsimp at y hy + -- The expected cofinal set `A` will be the range of `p.ι.app j'` + -- where `j' : J` is such that for any `x : X`, there is a map `j x ⟶ j'` + let j' := IsCardinalFiltered.max j hX + let y' (x : X) : (p.diag.obj j').obj := + p.diag.map (IsCardinalFiltered.toMax j hX x) (y x) + have hy' (x : X) : p.ι.app j' (y' x) = SetCardinalLT.singleton κ₁ x := by + rw [← hy, ← p.w (IsCardinalFiltered.toMax j hX x)] + rfl + refine ⟨Set.range (p.ι.app j'), (hp j').of_surjective _ + (Set.rangeFactorization_surjective (f := p.ι.app j')), fun ⟨B, hB⟩ ↦ ?_⟩ + let j'' := IsCardinalFiltered.max (fun b ↦ y' b.val) hB + refine ⟨_, ⟨j'', rfl⟩, fun b hb ↦ ?_⟩ + have : y' b ≤ j'' := (leOfHom (IsCardinalFiltered.toMax (fun b ↦ y' b.val) hB ⟨b, hb⟩) :) + refine (p.ι.app j').hom.hom.monotone this ?_ + convert Set.mem_singleton b + exact Subtype.ext_iff.1 (hy' b) + +section + +open CardinalDirectedPoset + +namespace existsIsCardinalFilteredSetOfExistsCofinal + +/-! The definitions in this section are part of the proof of the +lemma `exists_isCardinalFiltered_set_of_exists_cofinal` below, +which is the implication (iii) → (iv) in the characterizations +of `SharplyLT κ₁ κ₂` which appear in the docstring of this file. -/ + +variable (h₀ : κ₁ < κ₂) + {X : Type w} [PartialOrder X] + -- The variables `Y`, `hY` and `hY'` below can be obtained by applying + -- the `choose` tactic to an assumption of the form + -- `∃ (Y : Set (SetCardinalLT κ₁ X)), HasCardinalLT Y κ₂ ∧ IsCofinal Y)` + -- e.g. when the condition (iii) in the docstring of this file is satisfied + (Y : ∀ (B : Set X) (_ : HasCardinalLT B κ₂), Set (SetCardinalLT κ₁ B)) + (hY : ∀ (B : Set X) (hB : HasCardinalLT B κ₂), HasCardinalLT (Y B hB) κ₂) + (hY' : ∀ (B : Set X) (hB : HasCardinalLT B κ₂), IsCofinal (Y B hB)) + -- In the proof of `exists_isCardinalFiltered_set_of_exists_cofinal` below, + -- we shall show that we can find such `m` and `hm`, i.e. + -- for any `B : Set X`, `hB : HasCardinalLT B κ₂`, `C : SetCardinalLT κ₁ B`, + -- if `C ∈ Y B hB`, then there exists `m : X` such that all the elements + -- of `C` are less than or equal to `m`. + (m : ∀ (B : Set X) (hB : HasCardinalLT B κ₂) (C : SetCardinalLT κ₁ B), + C ∈ Y B hB → X) + (hm : ∀ (B : Set X) (hB : HasCardinalLT B κ₂) (C : SetCardinalLT κ₁ B) + (hC : C ∈ Y B hB) (b : B), b ∈ C.val → b ≤ m B hB C hC) + (A : Set X) (hA : HasCardinalLT A κ₂) + +/-- The subset of `X` given by the union over all `C : Y B hB` of +`C` and `{m B hB C _}`. -/ +def φ₀ (B : Set X) (hB : HasCardinalLT B κ₂) : Set X := + ⋃ (C : Y B hB), Subtype.val '' C.val.val ∪ {m B hB C C.prop} + +omit [Fact κ₁.IsRegular] [Fact κ₂.IsRegular] in +include hY' hm in +lemma hφ₀ (B : Set X) (hB : HasCardinalLT B κ₂) {T : Type w} (f : T → B) + (hT : HasCardinalLT T κ₁) : + ∃ (a : φ₀ Y m B hB), ∀ (t : T), f t ≤ a.val := by + let C₀ : SetCardinalLT κ₁ B := + ⟨Set.range f, hT.of_surjective _ Set.rangeFactorization_surjective⟩ + obtain ⟨C, hC, hC'⟩ := hY' B hB C₀ + exact ⟨⟨m B hB C hC, Set.subset_iUnion _ ⟨C, hC⟩ (Or.inr (by simp))⟩, + fun t ↦ hm B hB C hC (f t) (hC' (by simp [C₀]))⟩ + +open Classical in +/-- This coincides with `φ₀` when `HasCardinalLT B κ₂` holds. -/ +def φ (B : Set X) : Set X := + if hB : HasCardinalLT B κ₂ then φ₀ Y m B hB else B + +omit [Fact κ₁.IsRegular] [Fact κ₂.IsRegular] [PartialOrder X] in +lemma φ_eq (B : Set X) (hB : HasCardinalLT B κ₂) : + φ Y m B = φ₀ Y m B hB := dif_pos hB + +include hY' in +omit [Fact κ₂.IsRegular] [PartialOrder X] in +lemma le_φ (B : Set X) : B ≤ φ Y m B := by + dsimp [φ] + split_ifs with hB + · intro b hb + obtain ⟨C, hC, hC'⟩ := hY' B hB ⟨{⟨b, hb⟩}, + hasCardinalLT_of_finite _ _ (IsRegular.aleph0_le Fact.out)⟩ + refine Set.subset_iUnion _ ⟨C, hC⟩ (Or.inl ?_) + simp only [Set.mem_image, Subtype.exists, exists_and_right, exists_eq_right] + exact ⟨hb, @hC' ⟨b, hb⟩ (by simp)⟩ + · simp + +include h₀ hA hY in +omit [PartialOrder X] in +/-- By iterating `φ` to the power `j : κ₁.ord.ToType` and evaluating +on `A`, we get a subset that is of cardinality `< κ₂`. -/ +lemma hasCardinalLT_transfiniteIterate_φ (j : κ₁.ord.ToType) : + HasCardinalLT (transfiniteIterate (φ Y m) j A :) κ₂ := by + induction j using SuccOrder.limitRecOn with + | isMin j hj => + have := Cardinal.nonempty_ord_toType (c := κ₁) (IsRegular.ne_zero Fact.out) + letI := WellFoundedLT.toOrderBot κ₁.ord.ToType + simpa [hj.eq_bot] + | succ j hj hj' => + have hκ₂ : κ₂.IsRegular := Fact.out + rw [transfiniteIterate_succ _ _ _ hj, φ_eq _ _ _ hj'] + refine hasCardinalLT_iUnion _ (hY _ _) + (fun ⟨C, hC⟩ ↦ hasCardinalLT_union hκ₂.aleph0_le ?_ + (hasCardinalLT_of_finite _ _ hκ₂.aleph0_le)) + refine (C.prop.of_le h₀.le).of_injective (fun ⟨c, hc⟩ ↦ ?_) + (fun c₁ c₂ hc ↦ ?_) + · simp only [Set.mem_image, Subtype.exists, exists_and_right, exists_eq_right] at hc + exact ⟨⟨c, hc.choose⟩, hc.choose_spec⟩ + · simpa only [Subtype.ext_iff] using hc + | isSuccLimit j hj hj' => + rw [transfiniteIterate_limit _ _ _ hj, Set.iSup_eq_iUnion] + refine hasCardinalLT_iUnion _ + (HasCardinalLT.of_injective ?_ _ Subtype.val_injective) (fun ⟨k, hk⟩ ↦ hj' _ hk) + simpa [hasCardinalLT_iff_cardinal_mk_lt] + +include hY' in +omit [Fact κ₂.IsRegular] [PartialOrder X] in +lemma monotone_transfiniteIterate_φ : + Monotone (fun (j : κ₁.ord.ToType) ↦ transfiniteIterate (φ Y m) j A) := + have := Cardinal.nonempty_ord_toType (c := κ₁) (IsRegular.ne_zero Fact.out) + letI := WellFoundedLT.toOrderBot κ₁.ord.ToType + monotone_transfiniteIterate _ _ (le_φ _ hY' _) + +omit [PartialOrder X] [Fact κ₂.IsRegular] in +lemma subset_iUnion : A ⊆ ⋃ (j : κ₁.ord.ToType), transfiniteIterate (φ Y m) j A := by + have := Cardinal.nonempty_ord_toType (c := κ₁) (IsRegular.ne_zero Fact.out) + letI := WellFoundedLT.toOrderBot κ₁.ord.ToType + exact subset_trans (by simp) (Set.subset_iUnion _ ⊥) + +include h₀ hY hY' hm hA in +lemma isCardinalFiltered_iUnion : + IsCardinalFiltered (⋃ (j : κ₁.ord.ToType), transfiniteIterate (φ Y m) j A) κ₁ := by + suffices ∀ ⦃K : Type w⦄ (j : κ₁.ord.ToType) (f : K → (transfiniteIterate (φ Y m) j A : Set _)) + (hK : HasCardinalLT K κ₁), + ∃ (x : (transfiniteIterate (φ Y m) (Order.succ j) A : Set _)), + ∀ (k : K), (f k).val ≤ x.val by + refine isCardinalFiltered_preorder _ _ (fun K f hK ↦ ?_) + rw [← hasCardinalLT_iff_cardinal_mk_lt] at hK + have (k : K) : ∃ (j : κ₁.ord.ToType), (f k).val ∈ transfiniteIterate (φ Y m) j A := by + simpa only [Set.mem_iUnion] using (f k).prop + choose a ha using this + obtain ⟨⟨z, hz⟩, hz'⟩ := this (IsCardinalFiltered.max a hK) (fun k ↦ + ⟨(f k).val, monotone_transfiniteIterate_φ Y hY' m A + (leOfHom (IsCardinalFiltered.toMax a hK k)) (ha k)⟩) hK + exact ⟨⟨z, Set.subset_iUnion _ _ hz⟩, hz'⟩ + intro K j f hK + obtain ⟨⟨x, hx⟩, hx'⟩ := hφ₀ Y hY' m hm _ + (hasCardinalLT_transfiniteIterate_φ h₀ Y hY m A hA _) f hK + refine ⟨⟨x, ?_⟩, hx'⟩ + have : NoMaxOrder κ₁.ord.ToType := noMaxOrder (IsRegular.aleph0_le Fact.out) + rwa [transfiniteIterate_succ _ _ _ (not_isMax j), + φ_eq _ _ _ (hasCardinalLT_transfiniteIterate_φ h₀ Y hY m A hA _)] + +end existsIsCardinalFilteredSetOfExistsCofinal + +open existsIsCardinalFilteredSetOfExistsCofinal in +/-- This is the implication (iii) → (iv) in the characterizations +of `SharplyLT κ₁ κ₂` in the docstring of this file. -/ +public lemma exists_isCardinalFiltered_set_of_exists_cofinal (h₀ : κ₁ < κ₂) + (h : ∀ (X : Type w) (_ : HasCardinalLT X κ₂), + ∃ (Y : Set (SetCardinalLT κ₁ X)), HasCardinalLT Y κ₂ ∧ IsCofinal Y) + {X : Type w} [PartialOrder X] [IsCardinalFiltered X κ₁] + (A : Set X) (hA : HasCardinalLT A κ₂) : + ∃ (B : Set X), A ⊆ B ∧ IsCardinalFiltered B κ₁ ∧ HasCardinalLT B κ₂ := by + choose Y hY hY' using fun (B : Set X) hB ↦ h B hB + have hY'' (B : Set X) (hB : HasCardinalLT B κ₂) + (C : SetCardinalLT κ₁ B) (hC : C ∈ Y B hB) : + ∃ (m : X), ∀ (b : B), b ∈ C.val → b ≤ m := + ⟨IsCardinalFiltered.max (fun (c : C.val) ↦ c.val.val) C.prop, + fun b hb ↦ leOfHom (IsCardinalFiltered.toMax + (fun (c : C.val) ↦ c.val.val) C.prop ⟨_, hb⟩)⟩ + choose m hm using hY'' + -- The expected subset `B` is obtained as the union over + -- all `j : κ₁.ord.ToType` of the transfinite iterations + -- of the map `φ` + exact ⟨⋃ j, transfiniteIterate (φ Y m) j A, subset_iUnion Y m A, + isCardinalFiltered_iUnion h₀ Y hY hY' m hm A hA, + hasCardinalLT_iUnion _ (by simpa [hasCardinalLT_iff_cardinal_mk_lt]) + (hasCardinalLT_transfiniteIterate_φ h₀ Y hY m A hA)⟩ + +end + +end SharplyLT + +end Cardinal diff --git a/Mathlib/CategoryTheory/Presentable/Type.lean b/Mathlib/CategoryTheory/Presentable/Type.lean index 81f42fa1a9c37d..e7b611774ca7f3 100644 --- a/Mathlib/CategoryTheory/Presentable/Type.lean +++ b/Mathlib/CategoryTheory/Presentable/Type.lean @@ -5,8 +5,8 @@ Authors: Joël Riou -/ module -public import Mathlib.CategoryTheory.Presentable.Basic -public import Mathlib.CategoryTheory.Limits.Types.Filtered +public import Mathlib.CategoryTheory.Generator.Type +public import Mathlib.CategoryTheory.Presentable.StrongGenerator public import Mathlib.CategoryTheory.Types.Set /-! @@ -145,6 +145,27 @@ instance (X : Type u) : IsPresentable.{u} X := by have := hX.isCardinalPresentable exact isPresentable_of_isCardinalPresentable X κ +lemma isStrongGenerator_punit : + (ObjectProperty.singleton (PUnit.{u + 1})).IsStrongGenerator := by + rw [ObjectProperty.isStrongGenerator_iff] + refine ⟨isSeparator_punit, fun _ _ i hi₁ hi₂ ↦ ?_⟩ + · rw [mono_iff_injective] at hi₁ + rw [isIso_iff_bijective] + refine ⟨hi₁, fun y ↦ ?_⟩ + obtain ⟨f, hf⟩ := hi₂ PUnit ⟨.unit⟩ (↾fun _ ↦ y) + exact ⟨f .unit, ConcreteCategory.congr_hom hf .unit⟩ + +instance (κ : Cardinal.{u}) [Fact κ.IsRegular] : + IsCardinalLocallyPresentable (Type u) κ := by + rw [IsCardinalLocallyPresentable.iff_exists_isStrongGenerator] + exact ⟨.singleton PUnit, inferInstance, isStrongGenerator_punit, by + simp only [ObjectProperty.singleton_le_iff, + CategoryTheory.isCardinalPresentable_iff, isCardinalPresentable_iff] + exact hasCardinalLT_of_finite _ _ (Cardinal.IsRegular.aleph0_le Fact.out)⟩ + +instance : IsLocallyPresentable.{u} (Type u) where + exists_cardinal := ⟨_, Cardinal.fact_isRegular_aleph0, inferInstance⟩ + end Types end CategoryTheory diff --git a/Mathlib/CategoryTheory/Retract.lean b/Mathlib/CategoryTheory/Retract.lean index 455b39b836faaa..c0986d2ae21ec4 100644 --- a/Mathlib/CategoryTheory/Retract.lean +++ b/Mathlib/CategoryTheory/Retract.lean @@ -32,6 +32,11 @@ structure Retract (X Y : C) where r : Y ⟶ X retract : i ≫ r = 𝟙 X := by cat_disch +to_dual_name_hint I R, IArrow RArrow, Left Right + +attribute [to_dual existing] Retract.i +attribute [to_dual self] Retract.mk + namespace Retract attribute [reassoc (attr := simp)] retract @@ -47,6 +52,8 @@ def op : Retract (op X) (op Y) where r := h.i.op retract := by simp [← op_comp, h.retract] +attribute [to_dual existing] op_i + /-- If `X` is a retract of `Y`, then `F.obj X` is a retract of `F.obj Y`. -/ @[simps] def map (F : C ⥤ D) : Retract (F.obj X) (F.obj Y) where @@ -54,18 +61,16 @@ def map (F : C ⥤ D) : Retract (F.obj X) (F.obj Y) where r := F.map h.r retract := by rw [← F.map_comp h.i h.r, h.retract, F.map_id] +attribute [to_dual existing] map_i + /-- a retract determines a split epimorphism. -/ -@[simps] def splitEpi : SplitEpi h.r where +@[to_dual (attr := simps)/-- a retract determines a split monomorphism. -/] +def splitEpi : SplitEpi h.r where section_ := h.i -/-- a retract determines a split monomorphism. -/ -@[simps] def splitMono : SplitMono h.i where - retraction := h.r - +@[to_dual] instance : IsSplitEpi h.r := ⟨⟨h.splitEpi⟩⟩ -instance : IsSplitMono h.i := ⟨⟨h.splitMono⟩⟩ - variable (X) in /-- Any object is a retract of itself. -/ @[simps] @@ -73,12 +78,16 @@ def refl : Retract X X where i := 𝟙 X r := 𝟙 X +attribute [to_dual existing] refl_i + /-- A retract of a retract is a retract. -/ @[simps] def trans {Z : C} (h' : Retract Y Z) : Retract X Z where i := h.i ≫ h'.i r := h'.r ≫ h.r +attribute [to_dual existing] trans_i + /-- If `e : X ≅ Y`, then `X` is a retract of `Y`. -/ def ofIso (e : X ≅ Y) : Retract X Y where i := e.hom @@ -98,6 +107,7 @@ end Retract ``` A morphism `f : X ⟶ Y` is a retract of `g : Z ⟶ W` if there are morphisms `i : f ⟶ g` and `r : g ⟶ f` in the arrow category such that `i ≫ r = 𝟙 f`. -/ +@[to_dual self] abbrev RetractArrow {X Y Z W : C} (f : X ⟶ Y) (g : Z ⟶ W) := Retract (Arrow.mk f) (Arrow.mk g) namespace RetractArrow @@ -105,60 +115,58 @@ namespace RetractArrow variable {X Y Z W : C} {f : X ⟶ Y} {g : Z ⟶ W} (h : RetractArrow f g) set_option backward.isDefEq.respectTransparency false in -- This is needed for `MorphismProperty/Retract.lean` -@[reassoc] +@[to_dual none, reassoc] lemma i_w : h.i.left ≫ g = f ≫ h.i.right := h.i.w -@[reassoc] +@[to_dual none, reassoc] lemma r_w : h.r.left ≫ f = g ≫ h.r.right := h.r.w /-- The top of a retract diagram of morphisms determines a retract of objects. -/ -@[simps!] +@[to_dual (attr := simps!) +/-- The bottom of a retract diagram of morphisms determines a retract of objects. -/] def left : Retract X Z := h.map Arrow.leftFunc -/-- The bottom of a retract diagram of morphisms determines a retract of objects. -/ -@[simps!] -def right : Retract Y W := h.map Arrow.rightFunc - -@[reassoc (attr := simp)] +@[to_dual (attr := reassoc (attr := simp))] lemma retract_left : h.i.left ≫ h.r.left = 𝟙 X := h.left.retract -@[reassoc (attr := simp)] -lemma retract_right : h.i.right ≫ h.r.right = 𝟙 Y := h.right.retract - +@[to_dual] instance : IsSplitEpi h.r.left := ⟨⟨h.left.splitEpi⟩⟩ +@[to_dual] instance : IsSplitEpi h.r.right := ⟨⟨h.right.splitEpi⟩⟩ -instance : IsSplitMono h.i.left := ⟨⟨h.left.splitMono⟩⟩ - -instance : IsSplitMono h.i.right := ⟨⟨h.right.splitMono⟩⟩ - /-- If a morphism `f` is a retract of `g`, then `F.map f` is a retract of `F.map g` for any functor `F`. -/ -@[simps!] +@[to_dual self, simps!] def map (F : C ⥤ D) : RetractArrow (F.map f) (F.map g) := Retract.map h F.mapArrow +attribute [to_dual existing] map_i_left map_i_right + set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in /-- If a morphism `f` is a retract of `g`, then `f.op` is a retract of `g.op`. -/ -@[simps] +@[to_dual self, simps] def op : RetractArrow f.op g.op where i := Arrow.homMk (h.r.right.op) (h.r.left.op) (by simp [← op_comp]) r := Arrow.homMk (h.i.right.op) (h.i.left.op) (by simp [← op_comp]) retract := by ext <;> simp [← op_comp] +attribute [to_dual existing (reorder := X Y, Z W)] op_i + set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in /-- If a morphism `f` in the opposite category is a retract of `g`, then `f.unop` is a retract of `g.unop`. -/ -@[simps] +@[to_dual self, simps] def unop {X Y Z W : Cᵒᵖ} {f : X ⟶ Y} {g : Z ⟶ W} (h : RetractArrow f g) : RetractArrow f.unop g.unop where i := Arrow.homMk (h.r.right.unop) (h.r.left.unop) (by simp [← unop_comp]) r := Arrow.homMk (h.i.right.unop) (h.i.left.unop) (by simp [← unop_comp]) retract := by ext <;> simp [← unop_comp] +attribute [to_dual existing (reorder := X Y, Z W)] unop_i + end RetractArrow namespace Iso @@ -169,16 +177,20 @@ def retract {X Y : C} (e : X ≅ Y) : Retract X Y where i := e.hom r := e.inv +attribute [to_dual existing] retract_i + end Iso set_option backward.defeqAttrib.useBackward true in /-- If `X` is a retract of `Y`, then for any natural transformation `τ`, the natural transformation `τ.app X` is a retract of `τ.app Y`. -/ -@[simps] +@[to_dual self, simps] def NatTrans.retractArrowApp {F G : C ⥤ D} (τ : F ⟶ G) {X Y : C} (h : Retract X Y) : RetractArrow (τ.app X) (τ.app Y) where i := Arrow.homMk (F.map h.i) (G.map h.i) (by simp) r := Arrow.homMk (F.map h.r) (G.map h.r) (by simp) retract := by ext <;> simp [← Functor.map_comp] +attribute [to_dual existing (reorder := F G)] NatTrans.retractArrowApp_i + end CategoryTheory diff --git a/Mathlib/CategoryTheory/Shift/ShiftSequence.lean b/Mathlib/CategoryTheory/Shift/ShiftSequence.lean index 47ada4ddad480f..a94a1a3ff4b091 100644 --- a/Mathlib/CategoryTheory/Shift/ShiftSequence.lean +++ b/Mathlib/CategoryTheory/Shift/ShiftSequence.lean @@ -5,7 +5,7 @@ Authors: Joël Riou -/ module -public import Mathlib.CategoryTheory.Shift.Basic +public import Mathlib.CategoryTheory.Shift.CommShift public import Mathlib.CategoryTheory.Preadditive.AdditiveFunctor /-! # Sequences of functors from a category equipped with a shift @@ -33,8 +33,9 @@ set_option backward.defeqAttrib.useBackward true open CategoryTheory Category ZeroObject Limits -variable {C A : Type*} [Category* C] [Category* A] (F : C ⥤ A) - (M : Type*) [AddMonoid M] [HasShift C M] +variable {C D A : Type*} [Category* C] [Category* D] [Category* A] (F : C ⥤ A) + {π : C ⥤ D} {H : D ⥤ A} (e : π ⋙ H ≅ F) + (M : Type*) [AddMonoid M] [HasShift C M] [HasShift D M] {G : Type*} [AddGroup G] [HasShift C G] namespace CategoryTheory @@ -283,6 +284,37 @@ end end +namespace ShiftSequence + +variable {F} in +set_option backward.isDefEq.respectTransparency false in +/-- Given an isomorphism `π ⋙ H ≅ F`, where `π` is a functor which commutes +with the shift by `M` and `H` is equipped with a shift sequence, +then this is the shift sequence for `F` induced by composition. -/ +@[implicit_reducible, simps] +def leftComp [π.CommShift M] [H.ShiftSequence M] : F.ShiftSequence M where + sequence n := π ⋙ H.shift n + isoZero := isoWhiskerLeft π (H.isoShiftZero M) ≪≫ e + shiftIso n a a' ha' := + (Functor.associator _ _ _).symm ≪≫ + isoWhiskerRight (π.commShiftIso n) _ ≪≫ Functor.associator _ _ _ ≪≫ + isoWhiskerLeft π (H.shiftIso n a a' ha') + shiftIso_zero a := by + ext K + simp [← Functor.map_comp, commShiftIso_zero] + shiftIso_add n m a a' a'' ha' ha'':= by + ext K + dsimp + simp only [H.shiftIso_add_hom_app n m a a' a'' ha' ha'', assoc, + commShiftIso_add, CommShift.isoAdd_hom_app, ← Functor.map_comp_assoc, + id_comp, Iso.inv_hom_id_app, comp_obj, comp_id] + simp + +instance [π.CommShift M] [H.ShiftSequence M] : (π ⋙ H).ShiftSequence M := + leftComp (Iso.refl _) _ + +end ShiftSequence + end Functor end CategoryTheory diff --git a/Mathlib/CategoryTheory/ShrinkYoneda.lean b/Mathlib/CategoryTheory/ShrinkYoneda.lean index 9de58282348005..40a9ffa06c34ff 100644 --- a/Mathlib/CategoryTheory/ShrinkYoneda.lean +++ b/Mathlib/CategoryTheory/ShrinkYoneda.lean @@ -69,6 +69,8 @@ end FunctorToTypes variable [LocallySmall.{w} C] +section Yoneda + set_option backward.defeqAttrib.useBackward true in instance (X : C) : FunctorToTypes.Small.{w} (yoneda.obj X) := fun _ ↦ by dsimp; infer_instance @@ -85,13 +87,16 @@ noncomputable def shrinkYonedaObjObjEquiv {X : C} {Y : Cᵒᵖ} : ((shrinkYoneda.{w}.obj X).obj Y) ≃ (Y.unop ⟶ X) := (equivShrink _).symm -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in +lemma shrinkYoneda_obj_map {X : C} {Y Y' : Cᵒᵖ} (g : Y ⟶ Y') (f : (shrinkYoneda.obj X).obj Y) : + (shrinkYoneda.obj _).map g f = + shrinkYonedaObjObjEquiv.symm (g.unop ≫ shrinkYonedaObjObjEquiv f) := + rfl + lemma shrinkYoneda_obj_map_shrinkYonedaObjObjEquiv_symm {X : C} {Y Y' : Cᵒᵖ} (g : Y ⟶ Y') (f : Y.unop ⟶ X) : (shrinkYoneda.obj _).map g (shrinkYonedaObjObjEquiv.symm f) = shrinkYonedaObjObjEquiv.symm (g.unop ≫ f) := by - simp [shrinkYoneda, shrinkYonedaObjObjEquiv] + simp [shrinkYoneda_obj_map] lemma shrinkYonedaObjObjEquiv_symm_comp {X Y Y' : C} (g : Y' ⟶ Y) (f : Y ⟶ X) : shrinkYonedaObjObjEquiv.symm (g ≫ f) = @@ -249,4 +254,191 @@ def shrinkYonedaRepresentableBy (X : C) : (shrinkYoneda.{w}.obj X).Representable instance (X : C) : (shrinkYoneda.{w}.obj X).IsRepresentable := (shrinkYonedaRepresentableBy X).isRepresentable +end Yoneda + +section Coyoneda + +set_option backward.defeqAttrib.useBackward true in +instance (X : Cᵒᵖ) : FunctorToTypes.Small.{w} (coyoneda.obj X) := + fun _ ↦ by dsimp; infer_instance + +/-- The co-Yoneda embedding `Cᵒᵖ ⥤ C ⥤ Type w` for a locally `w`-small category `C`. -/ +@[pp_with_univ] +noncomputable abbrev shrinkCoyoneda : Cᵒᵖ ⥤ C ⥤ Type w := shrinkYoneda.flip + +lemma shrinkCoyoneda_obj {X : Cᵒᵖ} : + shrinkCoyoneda.obj X = FunctorToTypes.shrink (coyoneda.obj X) := rfl + +lemma shrinkCoyoneda_map {X Y : Cᵒᵖ} {f : X ⟶ Y} : + shrinkCoyoneda.map f = FunctorToTypes.shrinkMap (coyoneda.map f) := rfl + +/-- The type `(shrinkCoyoneda.obj X).obj Y` is equivalent to `X.unop ⟶ Y`. -/ +noncomputable abbrev shrinkCoyonedaObjObjEquiv {X : Cᵒᵖ} {Y : C} : + ((shrinkCoyoneda.{w}.obj X).obj Y) ≃ (X.unop ⟶ Y) := + shrinkYonedaObjObjEquiv + +lemma shrinkCoyoneda_obj_map {X : Cᵒᵖ} {Y Y' : C} (g : Y ⟶ Y') (f : (shrinkCoyoneda.obj X).obj Y) : + (shrinkCoyoneda.obj _).map g f = + shrinkCoyonedaObjObjEquiv.symm (shrinkCoyonedaObjObjEquiv f ≫ g) := + rfl + +lemma shrinkCoyoneda_obj_map_shrinkCoyonedaObjObjEquiv_symm + {X : Cᵒᵖ} {Y Y' : C} (g : Y ⟶ Y') (f : X.unop ⟶ Y) : + (shrinkCoyoneda.obj _).map g (shrinkCoyonedaObjObjEquiv.symm f) = + shrinkCoyonedaObjObjEquiv.symm (f ≫ g) := + shrinkYoneda_map_app_shrinkYonedaObjObjEquiv_symm f g + +lemma shrinkCoyonedaObjObjEquiv_symm_comp {X Y Y' : C} (g : Y' ⟶ Y) (f : Y ⟶ X) : + shrinkCoyonedaObjObjEquiv.symm (g ≫ f) = + (shrinkCoyoneda.obj _).map f (shrinkCoyonedaObjObjEquiv.symm g) := + (shrinkCoyoneda_obj_map_shrinkCoyonedaObjObjEquiv_symm f g).symm + +lemma shrinkCoyoneda_map_app_shrinkCoyonedaObjObjEquiv_symm + {X X' : Cᵒᵖ} {Y : C} (f : X.unop ⟶ Y) (g : X ⟶ X') : + (shrinkCoyoneda.map g).app _ (shrinkCoyonedaObjObjEquiv.symm f) = + shrinkCoyonedaObjObjEquiv.symm (g.unop ≫ f) := + shrinkYoneda_obj_map_shrinkYonedaObjObjEquiv_symm g f + +@[reassoc] +lemma shrinkCoyonedaObjObjEquiv_map_app + {X X' : Cᵒᵖ} {Y : C} (f : (shrinkCoyoneda.{w, v, u}.obj X).obj Y) (g : X ⟶ X') : + shrinkCoyonedaObjObjEquiv ((shrinkCoyoneda.map g).app Y f) = + g.unop ≫ shrinkCoyonedaObjObjEquiv f := + shrinkYonedaObjObjEquiv_obj_map g f + +@[reassoc] +lemma shrinkCoyonedaObjObjEquiv_obj_map {X : Cᵒᵖ} {Y Y' : C} (g : Y ⟶ Y') + (f : (shrinkCoyoneda.{w}.obj X).obj Y) : + shrinkCoyonedaObjObjEquiv ((shrinkCoyoneda.{w}.obj X).map g f) = + shrinkCoyonedaObjObjEquiv f ≫ g := + shrinkYonedaObjObjEquiv_map_app f g + +set_option backward.isDefEq.respectTransparency false in +/-- The type of natural transformations `shrinkCoyoneda.{w}.obj X ⟶ P` +with `X : Cᵒᵖ` and `P : C ⥤ Type w` is equivalent to `P.obj (op X)`. -/ +noncomputable def shrinkCoyonedaEquiv {X : Cᵒᵖ} {P : C ⥤ Type w} : + (shrinkCoyoneda.{w}.obj X ⟶ P) ≃ P.obj X.unop where + toFun τ := τ.app _ (equivShrink.{w} _ (𝟙 X.unop)) + invFun x := + { app Y := ↾fun f ↦ P.map ((equivShrink.{w} _).symm f) x + naturality Y Z g := by ext; simp [shrinkYoneda] } + left_inv τ := by + ext Y f + obtain ⟨f, rfl⟩ := (equivShrink _).surjective f + simpa [shrinkYoneda] using ((τ.naturality_apply f) (equivShrink _ (𝟙 X.unop))).symm + right_inv x := by simp + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +lemma map_shrinkCoyonedaEquiv {X Y : Cᵒᵖ} {P : C ⥤ Type w} (f : shrinkCoyoneda.obj X ⟶ P) + (g : Y ⟶ X) : P.map g.unop (shrinkCoyonedaEquiv f) = + f.app Y.unop (shrinkCoyonedaObjObjEquiv.symm g.unop) := by + simp [shrinkYonedaObjObjEquiv, shrinkCoyonedaEquiv, shrinkYoneda, + ← comp_apply, ← NatTrans.naturality] + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +lemma shrinkCoyonedaEquiv_shrinkCoyoneda_map {X Y : Cᵒᵖ} (f : X ⟶ Y) : + shrinkCoyonedaEquiv (shrinkCoyoneda.{w}.map f) = shrinkCoyonedaObjObjEquiv.symm f.unop := by + simp [shrinkCoyonedaEquiv, shrinkYoneda, shrinkYonedaObjObjEquiv] + +lemma shrinkCoyonedaEquiv_comp {X : Cᵒᵖ} {P Q : C ⥤ Type w} (α : shrinkCoyoneda.obj X ⟶ P) + (β : P ⟶ Q) : + shrinkCoyonedaEquiv (α ≫ β) = β.app _ (shrinkCoyonedaEquiv α) := by + simp [shrinkCoyonedaEquiv] + +set_option backward.isDefEq.respectTransparency false in +lemma shrinkCoyonedaEquiv_naturality {X Y : Cᵒᵖ} {P : C ⥤ Type w} + (f : shrinkCoyoneda.obj X ⟶ P) (g : Y ⟶ X) : + P.map g.unop (shrinkCoyonedaEquiv f) = shrinkCoyonedaEquiv (shrinkCoyoneda.map g ≫ f) := by + simpa [shrinkCoyonedaEquiv, shrinkYoneda] + using (f.naturality_apply g.unop ((equivShrink _) (𝟙 _))).symm + +@[reassoc] +lemma shrinkCoyonedaEquiv_symm_map {X Y : C} (f : X ⟶ Y) {P : C ⥤ Type w} (t : P.obj X) : + shrinkCoyonedaEquiv.symm (P.map f t) = + shrinkCoyoneda.map f.op ≫ shrinkCoyonedaEquiv.symm t := + shrinkCoyonedaEquiv.injective (by + obtain ⟨t, rfl⟩ := shrinkCoyonedaEquiv.surjective t + rw [← shrinkCoyonedaEquiv_naturality] + simp) + +lemma shrinkCoyonedaEquiv_symm_app_shrinkCoyonedaObjObjEquiv_symm {X : Cᵒᵖ} {P : C ⥤ Type w} + (s : P.obj X.unop) {Y : Cᵒᵖ} (f : Y ⟶ X) : + (shrinkCoyonedaEquiv.symm s).app Y.unop (shrinkCoyonedaObjObjEquiv.symm f.unop) = + P.map f.unop s := by + obtain ⟨g, rfl⟩ := shrinkCoyonedaEquiv.surjective s + simp [map_shrinkCoyonedaEquiv] + +variable (C) in +/-- The functor `shrinkCoyoneda : Cᵒᵖ ⥤ C ⥤ Type w` for a locally `w`-small category `C` +is fully faithful. -/ +noncomputable def fullyFaithfulShrinkCoyoneda : + (shrinkCoyoneda.{w} (C := C)).FullyFaithful where + preimage f := (shrinkCoyonedaObjObjEquiv (shrinkCoyonedaEquiv f)).op + map_preimage f := by + obtain ⟨f, rfl⟩ := shrinkCoyonedaEquiv.symm.surjective f + cat_disch + preimage_map f := by simp [shrinkCoyonedaEquiv_shrinkCoyoneda_map] + +instance : (shrinkCoyoneda.{w} (C := C)).Faithful := (fullyFaithfulShrinkCoyoneda C).faithful + +instance : (shrinkCoyoneda.{w} (C := C)).Full := (fullyFaithfulShrinkCoyoneda C).full + +set_option backward.defeqAttrib.useBackward true in +/-- `shrinkCoyoneda` at the morphism universe level is `coyoneda`. -/ +@[simps! hom_app inv_app] +noncomputable +def shrinkCoyonedaIsoCoyoneda : shrinkCoyoneda.{v} ≅ coyoneda (C := C) := + NatIso.ofComponents + (fun X ↦ NatIso.ofComponents (fun Y ↦ shrinkCoyonedaObjObjEquiv.toIso) + (by intros; ext; simp [shrinkYonedaObjObjEquiv_map_app])) + (by intros; ext; simp [shrinkYonedaObjObjEquiv_obj_map]) + +set_option backward.defeqAttrib.useBackward true in +/-- `shrinkCoyoneda` is compatible with `uliftFunctor`. -/ +noncomputable +def shrinkCoyonedaUliftFunctorIso [LocallySmall.{max w w'} C] : + shrinkCoyoneda.{w} ⋙ (Functor.whiskeringRight Cᵒᵖ _ _).obj uliftFunctor.{w', w} ≅ + shrinkCoyoneda := + NatIso.ofComponents + (fun X ↦ FunctorToTypes.shrinkCompUliftFunctorIso.{w, v} (coyoneda.obj X)) + fun _ ↦ by ext; simp [shrinkYoneda] + +/-- `uliftCoyoneda` identifies to `shrinkCoyoneda`. -/ +noncomputable def uliftYonedaIsoShrinkCoyoneda : + uliftCoyoneda.{w'} (C := C) ≅ shrinkCoyoneda.{max w' v} := + NatIso.ofComponents (fun X ↦ NatIso.ofComponents + (fun Y ↦ (Equiv.ulift.trans shrinkCoyonedaObjObjEquiv.symm).toIso) (fun f ↦ by + ext + exact (shrinkCoyoneda_obj_map_shrinkCoyonedaObjObjEquiv_symm _ _).symm)) (fun g ↦ by + ext + exact (shrinkCoyoneda_map_app_shrinkCoyonedaObjObjEquiv_symm _ _).symm) + +set_option backward.defeqAttrib.useBackward true in +/-- The functor `shrinkCoyoneda.{w}` followed by the evaluation +at `Y : C` and `uliftFunctor.{v}` identifies to `yoneda.obj Y` followed +by `uliftFunctor.{w}`. -/ +noncomputable def shrinkCoyonedaCompEvaluationCompUliftFunctorIsoUliftFunctor (Y : C) : + shrinkCoyoneda.{w} ⋙ (evaluation C _).obj Y ⋙ uliftFunctor.{v} ≅ + yoneda.obj Y ⋙ uliftFunctor.{w} := + NatIso.ofComponents (fun X ↦ (Equiv.ulift.trans + (shrinkCoyonedaObjObjEquiv.trans Equiv.ulift.symm)).toIso) (fun f ↦ by + ext ⟨g⟩ + obtain ⟨g, rfl⟩ := shrinkCoyonedaObjObjEquiv.symm.surjective g + simp [shrinkYoneda, shrinkYonedaObjObjEquiv]) + +/-- `shrinkCoyoneda.obj X` is corepresented by `X`. -/ +@[simps] +noncomputable +def shrinkCoyonedaCorepresentableBy (X : Cᵒᵖ) : + (shrinkCoyoneda.{w}.obj X).CorepresentableBy X.unop where + homEquiv := shrinkCoyonedaObjObjEquiv.symm + homEquiv_comp f g := shrinkCoyonedaObjObjEquiv_symm_comp g f + +instance (X : Cᵒᵖ) : (shrinkCoyoneda.{w}.obj X).IsCorepresentable := + (shrinkCoyonedaCorepresentableBy X).isCorepresentable + +end Coyoneda + end CategoryTheory diff --git a/Mathlib/CategoryTheory/Simple.lean b/Mathlib/CategoryTheory/Simple.lean index f77beb3d2d1d1b..b433d6015c5c0f 100644 --- a/Mathlib/CategoryTheory/Simple.lean +++ b/Mathlib/CategoryTheory/Simple.lean @@ -58,6 +58,13 @@ class Simple (X : C) : Prop where theorem isIso_of_mono_of_nonzero {X Y : C} [Simple Y] {f : X ⟶ Y} [Mono f] (w : f ≠ 0) : IsIso f := (Simple.mono_isIso_iff_nonzero f).mpr w +theorem Functor.simple_of_simple_obj {D : Type*} [Category* D] [HasZeroMorphisms D] (F : C ⥤ D) + [F.PreservesMonomorphisms] [F.PreservesZeroMorphisms] [F.ReflectsIsomorphisms] [F.Faithful] + (X : C) [Simple (F.obj X)] : Simple X := + .mk fun {Y} g _ ↦ by + rw [← isIso_iff_of_reflects_iso g F, Simple.mono_isIso_iff_nonzero (F.map g), + ne_eq, ne_eq, not_iff_not, F.map_eq_zero_iff] + theorem Simple.of_iso {X Y : C} [Simple Y] (i : X ≅ Y) : Simple X := { mono_isIso_iff_nonzero := fun f m => by constructor @@ -78,6 +85,19 @@ theorem Simple.of_iso {X Y : C} [Simple Y] (i : X ≅ Y) : Simple X := theorem Simple.iff_of_iso {X Y : C} (i : X ≅ Y) : Simple X ↔ Simple Y := ⟨fun _ => Simple.of_iso i.symm, fun _ => Simple.of_iso i⟩ +theorem simple_obj {D : Type*} [Category* D] [HasZeroMorphisms D] (F : C ⥤ D) + [F.IsEquivalence] (X : C) [Simple X] : Simple (F.obj X) := by + rw [← F.asEquivalence_functor] + have := F.asEquivalence.counitIso.app (F.asEquivalence.functor.obj X) + rw [Functor.comp_obj, Functor.id_obj] at this + have := Simple.of_iso <| Functor.preimageIso _ this + exact Functor.simple_of_simple_obj F.asEquivalence.inverse _ + +theorem simple_obj_iff {D : Type*} [Category* D] [HasZeroMorphisms D] (F : C ⥤ D) + [F.IsEquivalence] (X : C) : + Simple (F.obj X) ↔ Simple X := + ⟨fun _ ↦ Functor.simple_of_simple_obj F X, fun _ ↦ simple_obj F X⟩ + theorem kernel_zero_of_nonzero_from_simple {X Y : C} [Simple X] {f : X ⟶ Y} [HasKernel f] (w : f ≠ 0) : kernel.ι f = 0 := by classical @@ -142,7 +162,7 @@ theorem simple_of_cosimple (X : C) (h : ∀ {Z : C} (f : X ⟶ Z) [Epi f], IsIso have hx := cokernel.π_of_epi f by_contra h subst h - exact (h _).mp (cokernel.π_of_zero _ _) hx + exact (h _).mp inferInstance hx · intro hf suffices Epi f by exact isIso_of_mono_of_epi _ apply Preadditive.epi_of_cokernel_zero diff --git a/Mathlib/CategoryTheory/Sites/Abelian.lean b/Mathlib/CategoryTheory/Sites/Abelian.lean index 4eb95550cd3161..d5250edfc56c2d 100644 --- a/Mathlib/CategoryTheory/Sites/Abelian.lean +++ b/Mathlib/CategoryTheory/Sites/Abelian.lean @@ -11,6 +11,7 @@ public import Mathlib.CategoryTheory.Sites.ConstantSheaf /-! # Category of sheaves is abelian + Let `C, D` be categories and `J` be a Grothendieck topology on `C`, when `D` is abelian and sheafification is possible in `C`, `Sheaf J D` is abelian as well (`sheafIsAbelian`). diff --git a/Mathlib/CategoryTheory/Sites/Coherent/RegularSheaves.lean b/Mathlib/CategoryTheory/Sites/Coherent/RegularSheaves.lean index 994ca69622a51f..50b5cccb9a70d0 100644 --- a/Mathlib/CategoryTheory/Sites/Coherent/RegularSheaves.lean +++ b/Mathlib/CategoryTheory/Sites/Coherent/RegularSheaves.lean @@ -107,7 +107,7 @@ theorem EqualizerCondition.bijective_mapToEqualizer_pullback' {P : Cᵒᵖ ⥤ T simpa [mapToEqualizer] using! ha₁ · intro y h apply ha₂ y - simpa [mapToEqualizer, Subtype.ext_iff] using! h + simpa [mapToEqualizer] using Subtype.ext_iff.1 h theorem EqualizerCondition.bijective_mapToEqualizer_pullback {P : Cᵒᵖ ⥤ Type*} (hP : EqualizerCondition P) {X B : C} (π : X ⟶ B) [EffectiveEpi π] [HasPullback π π] : @@ -126,10 +126,11 @@ theorem EqualizerCondition.mk' (P : Cᵒᵖ ⥤ Type*) intro b hb obtain ⟨a, ha₁, ha₂⟩ := hP ⟨b, hb⟩ refine ⟨a, ?_, ?_⟩ - · simpa [Subtype.ext_iff, mapToEqualizer] using! ha₁ + · simpa [mapToEqualizer] using Subtype.ext_iff.1 ha₁ · intro y h apply ha₂ y - simpa [mapToEqualizer, Subtype.ext_iff] using! h + ext + simpa [mapToEqualizer] using h set_option backward.isDefEq.respectTransparency false in theorem EqualizerCondition.mk (P : Cᵒᵖ ⥤ Type*) diff --git a/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean b/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean index 05152c715b8756..e4fea004e81e05 100644 --- a/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean +++ b/Mathlib/CategoryTheory/Sites/ConcreteSheafification.lean @@ -122,7 +122,7 @@ theorem equiv_symm_eq_apply {X : C} {P : Cᵒᵖ ⥤ D} {S : J.Cover X} [HasMult -- We can hint `ConcreteCategory.hom (Y := P.obj (op I.Y))` below to put it into `simp`-normal -- form, but that doesn't seem to fix the `erw`s below... (Multiequalizer.ι (S.index P) I) ((Meq.equiv P S).symm x) = x I := by - simp [- GrothendieckTopology.Cover.index_left, ← equiv_apply] + simp [-GrothendieckTopology.Cover.index_left, ← equiv_apply] end Meq diff --git a/Mathlib/CategoryTheory/Sites/Continuous.lean b/Mathlib/CategoryTheory/Sites/Continuous.lean index 51995a1d8a49f6..48d970342868c4 100644 --- a/Mathlib/CategoryTheory/Sites/Continuous.lean +++ b/Mathlib/CategoryTheory/Sites/Continuous.lean @@ -80,6 +80,11 @@ lemma map_id : E.map (𝟭 _) = E := lemma map_comp {D' : Type*} [Category* D'] (G : D ⥤ D') : E.map (F ⋙ G) = (E.map F).map G := rfl +lemma sieve₀_map : (E.map F).sieve₀ = Sieve.functorPushforward _ E.sieve₀ := by + rw [PreZeroHypercover.sieve₀, Sieve.ofArrows, ← PreZeroHypercover.presieve₀, + PreOneHypercover.map_toPreZeroHypercover, PreZeroHypercover.presieve₀_map, + Sieve.generate_map_eq_functorPushforward] + /-- If `F : C ⥤ D`, `P : Dᵒᵖ ⥤ A` and `E` is a 1-pre-hypercover of an object of `X`, then `(E.map F).multifork P` is a limit iff `E.multifork (F.op ⋙ P)` is a limit. -/ def isLimitMapMultiforkEquiv {A : Type u} [Category.{t} A] (P : Dᵒᵖ ⥤ A) : diff --git a/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean b/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean index 9ebc9725d813d7..7c09039b9dde8d 100644 --- a/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean +++ b/Mathlib/CategoryTheory/Sites/CoproductSheafCondition.lean @@ -86,7 +86,8 @@ lemma Presieve.isSheafFor_sigmaDesc_iff {ι : Type*} {X : ι → C} (f : ∀ i, [∀ i, HasPullback (f i) (Cofan.IsColimit.desc hc f)] (F : Cᵒᵖ ⥤ Type*) [PreservesLimit (Discrete.functor <| fun i ↦ op (X i)) F] - [PreservesLimit (Discrete.functor fun (ij : ι × ι) ↦ op (pullback (f ij.1) (f ij.2))) F] : + [PreservesLimit (Discrete.functor fun (ij : ι × ι) ↦ + op (Limits.pullback (f ij.1) (f ij.2))) F] : Presieve.IsSheafFor F (.singleton <| Cofan.IsColimit.desc hc f) ↔ Presieve.IsSheafFor F (.ofArrows X f) := by let E := PreZeroHypercover.mk _ _ f diff --git a/Mathlib/CategoryTheory/Sites/CoverLifting.lean b/Mathlib/CategoryTheory/Sites/CoverLifting.lean index daec56154214c6..7590a8bf0ece84 100644 --- a/Mathlib/CategoryTheory/Sites/CoverLifting.lean +++ b/Mathlib/CategoryTheory/Sites/CoverLifting.lean @@ -102,6 +102,13 @@ lemma Functor.IsCocontinuous.iff_of_iso {F G : C ⥤ D} (e : F ≅ G) : F.IsCocontinuous J K ↔ G.IsCocontinuous J K := ⟨fun _ ↦ .of_iso e, fun _ ↦ .of_iso e.symm⟩ +lemma CoverPreserving.of_comp_of_isCocontinuous {F : C ⥤ D} (G : D ⥤ E) + (h : CoverPreserving J L (F ⋙ G)) [G.IsCocontinuous K L] [G.Full] [G.Faithful] : + CoverPreserving J K F where + cover_preserve {U} S hS := by + refine K.superset_covering ?_ (G.cover_lift K _ (h.cover_preserve hS)) + rw [Sieve.functorPushforward_comp, Sieve.functorPullback_functorPushforward_eq G] + section variable {F : C ⥤ D} {G : D ⥤ C} diff --git a/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean b/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean index 1b889bb5c7f259..7f49ecb19f8495 100644 --- a/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean +++ b/Mathlib/CategoryTheory/Sites/EffectiveEpimorphic.lean @@ -82,7 +82,6 @@ lemma Presieve.EffectiveEpimorphic.isSheafFor_of_isRepresentable {X : C} {R : Pr exact hR _ set_option backward.defeqAttrib.useBackward true in -set_option backward.proofsInPublic true in /-- Implementation: This is a construction which will be used in the proof that the sieve generated by a single arrow is effective epimorphic if and only if @@ -197,7 +196,6 @@ lemma Sieve.generateFamily_eq {B : C} {α : Type*} (X : α → C) (π : (a : α) exact ⟨_, g, π a, ⟨a⟩, rfl⟩ set_option backward.defeqAttrib.useBackward true in -set_option backward.proofsInPublic true in /-- Implementation: This is a construction which will be used in the proof that the sieve generated by a family of arrows is effective epimorphic if and only if diff --git a/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean b/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean index 45384739957a84..78d156f96d8089 100644 --- a/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean +++ b/Mathlib/CategoryTheory/Sites/Hypercover/Zero.lean @@ -377,6 +377,56 @@ lemma Hom.sieve₀_le_sieve₀ {E F : PreZeroHypercover S} (f : E.Hom F) : E.sie lemma sieve₀_eq_of_iso {E F : PreZeroHypercover S} (e : E ≅ F) : E.sieve₀ = F.sieve₀ := le_antisymm e.hom.sieve₀_le_sieve₀ e.inv.sieve₀_le_sieve₀ +/-- The equivalence on index types induced by an isomorphism of pre-`0`-hypercovers. -/ +@[simps] +def equivOfIso {E F : PreZeroHypercover.{w} S} (e : E ≅ F) : E.I₀ ≃ F.I₀ where + toFun := e.hom.s₀ + invFun := e.inv.s₀ + left_inv _ := by simp + right_inv _ := by simp + +lemma mem_of_iso {K : Precoverage C} [K.IsStableUnderComposition] [K.HasIsos] {X : C} + {E F : PreZeroHypercover.{w} X} (e : E ≅ F) (hE : E.presieve₀ ∈ K X) : + F.presieve₀ ∈ K X := by + have : F.presieve₀ = + Presieve.ofArrows (fun (i : Σ (_ : F.I₀), Unit) ↦ _) (fun i ↦ e.inv.h₀ i.1 ≫ E.f _) := by + simp only [Hom.w₀] + refine le_antisymm ?_ ?_ + · rw [Presieve.ofArrows_le_iff] + intro i + exact .mk (⟨i, ⟨⟩⟩ : Σ (_ : F.I₀), Unit) + · simp [Presieve.ofArrows_le_iff] + rw [this] + refine K.comp_mem_coverings (fun i ↦ E.f (e.inv.s₀ i)) ?_ (fun i (k : Unit) ↦ e.inv.h₀ i) ?_ + · rwa [← E.presieve₀_reindex (PreZeroHypercover.equivOfIso e.symm)] at hE + · intro i + rw [Presieve.ofArrows_pUnit] + exact K.mem_coverings_of_isIso _ + +lemma mem_iff_of_iso {K : Precoverage C} [K.IsStableUnderComposition] [K.HasIsos] {X : C} + {E F : PreZeroHypercover.{w} X} (e : E ≅ F) : + E.presieve₀ ∈ K X ↔ F.presieve₀ ∈ K X := + ⟨fun h ↦ PreZeroHypercover.mem_of_iso e h, fun h ↦ PreZeroHypercover.mem_of_iso e.symm h⟩ + +/-- Compose a pre-`0`-hypercover with a morphism on the right. -/ +@[simps] +def pushforward {X Y : C} (f : X ⟶ Y) (E : PreZeroHypercover.{w} X) : + PreZeroHypercover.{w} Y where + I₀ := E.I₀ + X := E.X + f i := E.f i ≫ f + +lemma presieve₀_pushforward {X Y : C} (f : X ⟶ Y) (E : PreZeroHypercover.{w} X) : + (E.pushforward f).presieve₀ = E.presieve₀.pushforward f := by + simp [presieve₀, Presieve.pushforward_ofArrows, pushforward] + +set_option backward.isDefEq.respectTransparency false in +/-- Pushforward along a morphism is the same as refining the singleton pre-`0`-hypercover. -/ +@[simps!] +def pushforwardIsoBind {X Y : C} (f : X ⟶ Y) (E : PreZeroHypercover.{w} X) : + E.pushforward f ≅ (singleton f).bind fun _ ↦ E := + isoMk ((Equiv.uniqueSigma fun i ↦ E.I₀).symm) (fun _ ↦ Iso.refl _) + end Category section Functoriality @@ -728,6 +778,16 @@ def weaken {K L : Precoverage C} {X : C} (E : Precoverage.ZeroHypercover K X) (h __ := E mem₀ := h _ E.mem₀ +/-- Compose a `0`-hypercover with a morphism on the right. -/ +@[simps toPreZeroHypercover] +def pushforward [J.IsStableUnderComposition] [J.HasIsos] {X Y : C} (f : X ⟶ Y) + (hf : .singleton f ∈ J _) (E : ZeroHypercover.{w} J X) : + ZeroHypercover.{w} J Y where + __ := E.toPreZeroHypercover.pushforward f + mem₀ := by + rw [PreZeroHypercover.mem_iff_of_iso (E.pushforwardIsoBind _)] + exact ((ZeroHypercover.singleton f hf).bind _).mem₀ + instance (K : Precoverage C) [K.HasPullbacks] {X Y : C} (E : K.ZeroHypercover X) (f : Y ⟶ X) : E.presieve₀.HasPullbacks f := K.hasPullbacks_of_mem _ E.mem₀ @@ -855,6 +915,9 @@ lemma le_of_zeroHypercover {J K : Precoverage C} class Small (J : Precoverage C) : Prop where zeroHypercoverSmall : ∀ {S : C} (E : ZeroHypercover.{max u v} J S), ZeroHypercover.Small.{w'} E +instance (K : Precoverage C) : Small.{max u v} K where + zeroHypercoverSmall := inferInstance + instance (J : Precoverage C) [Small.{w} J] {S : C} (E : ZeroHypercover.{w'} J S) : ZeroHypercover.Small.{w} E := by have : ZeroHypercover.Small.{w} (ZeroHypercover.restrictIndexOfSmall.{max u v} E) := diff --git a/Mathlib/CategoryTheory/Sites/IsSheafFor.lean b/Mathlib/CategoryTheory/Sites/IsSheafFor.lean index 7044c4e243f591..c80ad160c9acd2 100644 --- a/Mathlib/CategoryTheory/Sites/IsSheafFor.lean +++ b/Mathlib/CategoryTheory/Sites/IsSheafFor.lean @@ -557,7 +557,7 @@ theorem isSheafFor_iff_yonedaSheafCondition {P : Cᵒᵖ ⥤ Type v₁} : dsimp [functor] simp only [Subtype.forall, shrinkYonedaObjObjEquiv.forall_congr_left, Equiv.apply_symm_apply] congr! - simp [Equiv.subtypeEquiv] + simp /-- If `P` is a sheaf for the sieve `S` on `X`, a natural transformation from `S` (viewed as a functor) @@ -1062,10 +1062,10 @@ theorem isSheafFor_trans (P : Cᵒᵖ ⥤ Type*) (R S : Sieve X) apply Presieve.isSheafFor_subsieve_aux P this · apply isSheafFor_bind _ _ _ hR hS intro Y f hf Z g - rw [← pullback_comp] + rw [← Sieve.pullback_comp] apply (hS (R.downward_closed hf _)).isSeparatedFor · intro Y f hf - have : Sieve.pullback f (Sieve.bind R fun T (k : T ⟶ X) (_ : R k) => pullback k S) = + have : Sieve.pullback f (Sieve.bind R fun T (k : T ⟶ X) (_ : R k) => Sieve.pullback k S) = R.pullback f := by ext Z g constructor diff --git a/Mathlib/CategoryTheory/Sites/LeftExact.lean b/Mathlib/CategoryTheory/Sites/LeftExact.lean index e44d4e52fd62da..2b595f5006ae30 100644 --- a/Mathlib/CategoryTheory/Sites/LeftExact.lean +++ b/Mathlib/CategoryTheory/Sites/LeftExact.lean @@ -12,6 +12,7 @@ public import Mathlib.CategoryTheory.Sites.ConcreteSheafification /-! # Left exactness of sheafification + In this file we show that sheafification commutes with finite limits. -/ diff --git a/Mathlib/CategoryTheory/Sites/LocalSite.lean b/Mathlib/CategoryTheory/Sites/LocalSite.lean new file mode 100644 index 00000000000000..5deba23c853f0a --- /dev/null +++ b/Mathlib/CategoryTheory/Sites/LocalSite.lean @@ -0,0 +1,213 @@ +/- +Copyright (c) 2025 Ben Eltschig. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Ben Eltschig +-/ +module + +public import Mathlib.CategoryTheory.Adjunction.Triple +public import Mathlib.CategoryTheory.Limits.Elements +public import Mathlib.CategoryTheory.Sites.GlobalSections +public import Mathlib.CategoryTheory.Sites.Point.Skyscraper + +/-! +# Local sites + +A site is called local if it has a terminal object whose only covering sieve is trivial - +this makes it possible to define coconstant sheaves on it, giving its sheaf topos the structure +of a local topos. This is one of the conditions of cohesive sites. + +Sheaves of types on any local site form a local topos (i.e. a topos whose global sections functor +has a fully faithful right adjoint), and a subcanonical site is local if and only if its topos of +sheaves of types is (see TODOs). + +## Main definitions / results + +* `J.IsLocalSite`: typeclass stating that `J` makes the category it is defined on into a local site. +* `IsLocalSite.point J`: the canonical point of any local site, whose fibre functor is given by + the coyoneda embedding of the terminal object and extends to the global sections functors on + presheaves and sheaves. +* `coconstantSheaf J A`: the coconstant sheaf functor `A ⥤ Sheaf J A` for any local site and + sufficiently nice target category `A`, defined as the skyscraper sheaf functor of the canonical + point. +* `ΓCoconstantSheafAdj J A`: the adjunction between the global sections functor `Γ J A` and + `coconstantSheaf J A`. +* `fullyFaithfulCoconstantSheaf`: `coconstantSheaf` is fully faithful. +* `fullyFaithfulConstantSheaf`: on local sites, the constant sheaf functor is fully faithful. + +## References + +* https://ncatlab.org/nlab/show/local+site + +## TODO + +* Define local topoi and prove that sheaves on any local site form a local topos +* Show that a subcanonical site is local if and only if its global sections functor has a fully + faithful right adjoint +-/ + +universe w u v u' v' + +@[expose] public section + +open CategoryTheory Limits Sheaf Opposite GrothendieckTopology + +namespace CategoryTheory + +variable {C : Type u} [Category.{v} C] (J : GrothendieckTopology C) + +/-- A local site is a site that has a terminal object with only a single covering sieve. -/ +class GrothendieckTopology.IsLocalSite extends HasTerminal C where + /-- The only covering sieve of the terminal object is the trivial sieve. -/ + eq_top_of_mem : ∀ S ∈ J (⊤_ C), S = ⊤ + +namespace GrothendieckTopology.IsLocalSite + +/-- On a local site, every covering sieve contains every morphism from the terminal object. -/ +lemma from_terminal_mem_of_mem [J.IsLocalSite] {X : C} (f : ⊤_ C ⟶ X) {S : Sieve X} + (hS : S ∈ J X) : S.arrows f := + (S.mem_iff_pullback_eq_top f).mpr <| eq_top_of_mem _ <| J.pullback_stable f hS + +/-- Every category with a terminal object becomes a local site with the trivial topology. -/ +instance {C : Type u} [Category.{v} C] [HasTerminal C] : (trivial C).IsLocalSite where + eq_top_of_mem _ := trivial_covering.mp + +/-- Every local site has a canonical point, given as a fibre functor by the coyoneda embedding of +the terminal object `⊤_ C`. -/ +noncomputable def point [LocallySmall.{w} C] [J.IsLocalSite] : Point.{w} J where + fiber := shrinkCoyoneda.obj (op (⊤_ C)) + jointly_surjective R hR x := + ⟨(⊤_ C), shrinkCoyonedaObjObjEquiv x, + (from_terminal_mem_of_mem J (shrinkCoyonedaObjObjEquiv x) hR), + shrinkCoyonedaObjObjEquiv.symm (𝟙 _), by + rw [shrinkCoyoneda_obj_map_shrinkCoyonedaObjObjEquiv_symm] + simp⟩ + +variable [LocallySmall.{w} C] [J.IsLocalSite] (A : Type u') [Category.{v'} A] + +/-- The right adjoint to the global sections functor that exists over any local site. This is +implemented as the skyscraper functor associated to `point.{w} J`, but can be thought of +as taking any object `X : A` to the sheaf that sends each `Y : C` to the product over copies of `A` +indexed by the points `⊤_ C ⟶ Y` of `Y`. + +Note this takes in an extra universe parameter `w` that does not appear in the output type +`A ⥤ Sheaf J A` but is required for the construction; it should always be given explicitly when +referring to this functor, as in e.g. `coconstantSheaf.{w} J A`. -/ +noncomputable def coconstantSheaf [HasProducts.{w} A] : A ⥤ Sheaf J A := + (point.{w} J).skyscraperSheafFunctor + +variable [HasColimitsOfSize.{w, w} A] + +variable {A} in +/-- The fibre of any presheaf `P : Cᵒᵖ ⥤ A` at `point J` is just `P` evaluated at +the terminal object. -/ +noncomputable def pointPresheafFiberIso (P : Cᵒᵖ ⥤ A) : + (point J).presheafFiber.obj P ≅ P.obj (op (⊤_ C)) := + (colimit.isColimit _).coconePointUniqueUpToIso + (colimitOfDiagramTerminal (Functor.Elements.isInitialOfCorepresentableBy + <| shrinkCoyonedaCorepresentableBy <| op (⊤_ C)).op _) + +variable {A} in +set_option backward.isDefEq.respectTransparency false in +set_option backward.defeqAttrib.useBackward true in +@[reassoc (attr := simp)] +lemma toPresheafFiber_pointPresheafFiberIso_hom {P : Cᵒᵖ ⥤ A} (X : C) (x : (point J).fiber.obj X) : + (point J).toPresheafFiber _ x _ ≫ (pointPresheafFiberIso J P).hom = + P.map (.op <| shrinkCoyonedaObjObjEquiv x) := by + simp [Point.toPresheafFiber, pointPresheafFiberIso] + rfl + +variable {A} in +@[reassoc (attr := simp)] +lemma pointPresheafFiberIso_naturality {P P' : Cᵒᵖ ⥤ A} (F : P ⟶ P') : + (point J).presheafFiber.map F ≫ (pointPresheafFiberIso J P').hom = + (pointPresheafFiberIso J P).hom ≫ F.app (op (⊤_ C)) := by + cat_disch + +/-- The presheaf fibre functor of `point J` is given by evaluation at the terminal +object. -/ +noncomputable def pointPresheafFiberNatIso : + ((point J).presheafFiber : _ ⥤ A) ≅ (evaluation _ _).obj (op (⊤_ C)) := + NatIso.ofComponents (pointPresheafFiberIso J) fun F ↦ pointPresheafFiberIso_naturality J F + +/-- The sheaf fibre functor of `point J` is the global sections functor. -/ +noncomputable def pointSheafFiberIso + [HasWeakSheafify J A] : (point J).sheafFiber ≅ Γ J A := + ((sheafToPresheaf J A).isoWhiskerLeft (pointPresheafFiberNatIso J A)).trans + (ΓNatIsoSheafSections J A terminalIsTerminal).symm + +variable [HasProducts.{w} A] [HasWeakSheafify J A] + +/-- On local sites, the global sections functor `Γ` is left-adjoint to the coconstant functor. -/ +noncomputable def ΓCoconstantSheafAdj : Γ J A ⊣ coconstantSheaf.{w} J A := + (point.{w} J).skyscraperSheafAdjunction.ofNatIsoLeft (pointSheafFiberIso J A) + +/-- On any locally `w`-small local site, the global sections functor to any category with colimits +and products of size `w` is a left adjoint. A variant of this without the universe parameter `w` +is registered as an instance. -/ +lemma Γ_isLeftAdjoint : (Γ J A).IsLeftAdjoint := + ⟨coconstantSheaf.{w} J A, ⟨ΓCoconstantSheafAdj J A⟩⟩ + +/-- On any local site with morphism types in `Type v`, the global sections functor to any category +with colimits and products of size `v` is a left adjoint. See `ΓIsLeftAdjoint` for a +version for `w`-locally small sites that can't be registered as an instance because of the extra +universe parameter `w`. -/ +instance (A : Type u') [Category.{v'} A] [HasColimitsOfSize.{v, v} A] + [HasProducts.{v} A] [HasWeakSheafify J A] : (Γ J A).IsLeftAdjoint := + Γ_isLeftAdjoint.{v} J A + +instance : (coconstantSheaf.{w} J A).IsRightAdjoint := + ⟨Γ J A, ⟨ΓCoconstantSheafAdj J A⟩⟩ + +set_option backward.defeqAttrib.useBackward true in +/-- The global sections of the coconstant sheaf on a type are naturally isomorphic to that type. -/ +noncomputable def coconstantSheafΓNatIsoId : + IsLocalSite.coconstantSheaf.{w} J A ⋙ Γ J A ≅ 𝟭 A := + letI : Unique (unop ((IsLocalSite.point J).fiber.op.obj (op (⊤_ C)))) := + (equivShrink (⊤_ C ⟶ ⊤_ C)).symm.unique + (Functor.isoWhiskerLeft _ (ΓNatIsoSheafSections J _ terminalIsTerminal)) ≪≫ + NatIso.ofComponents (fun X ↦ productUniqueIso _) (by simp [IsLocalSite.coconstantSheaf]) + +/-- `coconstantSheaf` is fully faithful. -/ +noncomputable def fullyFaithfulCoconstantSheaf : + (coconstantSheaf.{w} J A).FullyFaithful := + (ΓCoconstantSheafAdj J A).fullyFaithfulROfCompIsoId (coconstantSheafΓNatIsoId J A) + +instance : (coconstantSheaf.{w} J A).Full := + (fullyFaithfulCoconstantSheaf J A).full + +instance : (coconstantSheaf.{w} J A).Faithful := + (fullyFaithfulCoconstantSheaf J A).faithful + +/-- The adjoint triple `constantSheaf J A ⊣ Γ J A ⊣ coconstantSheaf J A` on any local site. -/ +noncomputable abbrev constantΓCoconstantTriple : + Adjunction.Triple (constantSheaf J A) (Γ J A) (coconstantSheaf.{w} J A) where + adj₁ := constantSheafΓAdj J A + adj₂ := ΓCoconstantSheafAdj J A + +/-- On local sites, the constant sheaf functor is fully faithful. -/ +noncomputable def fullyFaithfulConstantSheaf : (constantSheaf J A).FullyFaithful := + (constantΓCoconstantTriple J A).fullyFaithfulEquiv.symm <| + fullyFaithfulCoconstantSheaf.{w} J A + +lemma full_constantSheaf : (constantSheaf J A).Full := + (fullyFaithfulConstantSheaf.{w} J A).full + +lemma faithful_constantSheaf : (constantSheaf J A).Faithful := + (fullyFaithfulConstantSheaf.{w} J A).faithful + +/-- See `IsLocalSite.full_constantSheaf` for a version for `w`-locally small sites. -/ +instance {C : Type u} [Category.{v} C] (J : GrothendieckTopology C) [J.IsLocalSite] + (A : Type u') [Category.{v'} A] [HasColimitsOfSize.{v, v} A] + [HasProducts.{v} A] [HasWeakSheafify J A] : (constantSheaf J A).Full := + full_constantSheaf.{v} J A + +/-- See `IsLocalSite.faithful_constantSheaf` for a version for `w`-locally small sites. -/ +instance {C : Type u} [Category.{v} C] (J : GrothendieckTopology C) [J.IsLocalSite] + (A : Type u') [Category.{v'} A] [HasColimitsOfSize.{v, v} A] + [HasProducts.{v} A] [HasWeakSheafify J A] : (constantSheaf J A).Faithful := + faithful_constantSheaf.{v} J A + +end GrothendieckTopology.IsLocalSite + +end CategoryTheory diff --git a/Mathlib/CategoryTheory/Sites/MorphismProperty.lean b/Mathlib/CategoryTheory/Sites/MorphismProperty.lean index 7d6f1c72ace666..81bd3600419586 100644 --- a/Mathlib/CategoryTheory/Sites/MorphismProperty.lean +++ b/Mathlib/CategoryTheory/Sites/MorphismProperty.lean @@ -49,7 +49,7 @@ lemma ofArrows_mem_precoverage {X : C} {ι : Type*} {Y : ι → C} {f : ∀ i, Y @[simp, grind =] lemma singleton_mem_precoverage {X Y : C} (f : X ⟶ Y) : .singleton f ∈ precoverage P Y ↔ P f := by - simp [← Presieve.ofArrows_pUnit.{_, _, 0}] + simp [← Presieve.ofArrows_pUnit.{0}] instance [P.ContainsIdentities] [P.RespectsIso] : P.precoverage.HasIsos where mem_coverings_of_isIso f _ _ _ := fun ⟨⟩ ↦ P.of_isIso f diff --git a/Mathlib/CategoryTheory/Sites/Over.lean b/Mathlib/CategoryTheory/Sites/Over.lean index fcbd6425dcac7a..b008c77f36e1fb 100644 --- a/Mathlib/CategoryTheory/Sites/Over.lean +++ b/Mathlib/CategoryTheory/Sites/Over.lean @@ -361,6 +361,12 @@ instance {D : Type*} [Category* D] {J : GrothendieckTopology C} {K : Grothendiec ← PreOneHypercover.map_comp, Over.post_forget_eq_forget_comp, PreOneHypercover.map_comp] exact E'.mem₁ _ _ _ _ congr($(w).left) +instance {D : Type*} [Category* D] {J : GrothendieckTopology C} {K : GrothendieckTopology D} + {F : C ⥤ D} (X : C) (Y : D) (f : F.obj X ⟶ Y) + [(Over.post F).IsContinuous (J.over X) (K.over _)] : + (Over.post F ⋙ Over.map f).IsContinuous (J.over X) (K.over Y) := + Functor.isContinuous_comp _ _ _ (K.over _) _ + open Limits lemma coverPreserving_overPullback [HasPullbacks C] {X Y : C} (f : X ⟶ Y) : diff --git a/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean b/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean index 54e9c9b6f39918..d6e557e769855b 100644 --- a/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean +++ b/Mathlib/CategoryTheory/Sites/Point/Skyscraper.lean @@ -109,7 +109,7 @@ lemma skyscraperPresheafHomEquiv_naturality_left Φ.skyscraperPresheafHomEquiv (Φ.presheafFiber.map f ≫ g) = f ≫ Φ.skyscraperPresheafHomEquiv g := Φ.skyscraperPresheafHomEquiv.symm.injective - (by simp [Φ.skyscraperPresheafHomEquiv_naturality_left_symm]) + (by simp [Φ.skyscraperPresheafHomEquiv_naturality_left_symm]) end diff --git a/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean b/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean index 9ae4abb0b79848..fc344b9950c132 100644 --- a/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean +++ b/Mathlib/CategoryTheory/Sites/PrecoverageToGrothendieck.lean @@ -232,7 +232,7 @@ lemma Presieve.isSheafFor_singleton_iff_of_iso {F : Cᵒᵖ ⥤ Type*} {S X Y : (g : Y ⟶ S) (e : X ≅ Y) (he : e.hom ≫ g = f) : (singleton f).IsSheafFor F ↔ (singleton g).IsSheafFor F := by subst he - rw [← Presieve.ofArrows_pUnit.{_, _, 0}, ← Presieve.ofArrows_pUnit, + rw [← Presieve.ofArrows_pUnit.{0}, ← Presieve.ofArrows_pUnit, Presieve.isSheafFor_ofArrows_comp_iff] open Limits diff --git a/Mathlib/CategoryTheory/Sites/Sieves.lean b/Mathlib/CategoryTheory/Sites/Sieves.lean index 7838be1c2e50fd..45cd429c4147d0 100644 --- a/Mathlib/CategoryTheory/Sites/Sieves.lean +++ b/Mathlib/CategoryTheory/Sites/Sieves.lean @@ -271,7 +271,7 @@ lemma ofArrows_of_unique {X : C} {ι : Type*} [Unique ι] {Y : ι → C} (f : obtain rfl : i = default := Subsingleton.elim _ _ simp -theorem ofArrows_pUnit : (ofArrows _ fun _ : PUnit => f) = singleton f := by +theorem ofArrows_pUnit : (ofArrows _ fun _ : PUnit.{w + 1} => f) = singleton f := by rw [ofArrows_of_unique] @[grind =] @@ -295,6 +295,82 @@ lemma bindOfArrows_ofArrows {ι : Type*} {S : C} {X : ι → C} (f : (i : ι) rintro W u ⟨i, v, ⟨j⟩⟩ exact ⟨Sigma.mk i j⟩ +/-- Compose a presieve on the right with a morphism. -/ +def pushforward {X Y : C} (f : X ⟶ Y) (R : Presieve X) : Presieve Y := + fun Z fg ↦ ∃ (g : Z ⟶ X), g ≫ f = fg ∧ R g + +@[grind .] +lemma pushforward_apply_comp {X Y Z : C} {f : X ⟶ Y} {R : Presieve X} {g : Z ⟶ X} (hg : R g) : + R.pushforward f (g ≫ f) := + ⟨g, rfl, hg⟩ + +lemma pushforward_ofArrows {ι : Type*} {U : ι → C} {X Y : C} (g : ∀ i, U i ⟶ X) + (f : X ⟶ Y) : (ofArrows _ g).pushforward f = ofArrows _ (g · ≫ f) := by + refine le_antisymm ?_ ?_ + · rintro _ _ ⟨u, rfl, ⟨i⟩⟩ + exact ⟨i⟩ + · rw [ofArrows_le_iff] + intro i + use g i, rfl + exact ⟨i⟩ + +lemma pushforward_singleton {X Y Z : C} (f : X ⟶ Y) (g : Y ⟶ Z) : + (singleton f).pushforward g = .singleton (f ≫ g) := by + rw [← ofArrows_pUnit.{0}, pushforward_ofArrows, ofArrows_pUnit.{0}] + +/-- The pullback of a presieve `R` on `Y` along a morphism `f : X ⟶ Y` is the presieve on `X` +given by all morphisms `g : Z ⟶ X` such that `f ≫ g` is in `R`. -/ +def pullback {X Y : C} (f : X ⟶ Y) (R : Presieve Y) : Presieve X := + fun _ g ↦ R (g ≫ f) + +variable {f} in +@[simp, grind =] +lemma pullback_iff {R : Presieve X} {Z : C} {g : Z ⟶ Y} : + R.pullback f g ↔ R (g ≫ f) := + .rfl + +lemma pushforward_le_iff_le_pullback (R : Presieve Y) (T : Presieve X) : + R.pushforward f ≤ T ↔ R ≤ T.pullback f := by + refine ⟨fun hle Z g hg ↦ hle _ _ (pushforward_apply_comp hg), ?_⟩ + rintro hle Z - ⟨g, rfl, hg⟩ + exact hle _ _ hg + +lemma galoisConnection_pushforward_pullback : + GaloisConnection (pushforward f) (pullback f) := + pushforward_le_iff_le_pullback f + +lemma monotone_pushforward : Monotone (pushforward f) := + (galoisConnection_pushforward_pullback f).monotone_l + +lemma monotone_pullback : Monotone (pullback f) := + (galoisConnection_pushforward_pullback f).monotone_u + +lemma pushforward_pullback_le (R : Presieve X) : (R.pullback f).pushforward f ≤ R := + (galoisConnection_pushforward_pullback f).l_u_le _ + +lemma le_pullback_pushforward (R : Presieve Y) : R ≤ (R.pushforward f).pullback f := + (galoisConnection_pushforward_pullback f).le_u_l _ + +@[simp] +lemma pullback_id (R : Presieve X) : R.pullback (𝟙 X) = R := by + funext + simp + +lemma pullback_comp (R : Presieve Z) (g : X ⟶ Z) : + R.pullback (f ≫ g) = (R.pullback g).pullback f := by + funext + simp + +@[simp] +lemma pushforward_id (R : Presieve X) : R.pushforward (𝟙 X) = R := by + funext + simp [pushforward] + +lemma pushforward_comp (R : Presieve Y) (g : X ⟶ Z) : + R.pushforward (f ≫ g) = (R.pushforward f).pushforward g := by + funext + simp [pushforward] + /-- Given a presieve on `F(X)`, we can define a presieve on `X` by taking the preimage via `F`. -/ def functorPullback (R : Presieve (F.obj X)) : Presieve X := fun _ f => R (F.map f) @@ -399,7 +475,7 @@ lemma map_ofArrows {X : C} {ι : Type*} {Y : ι → C} (f : ∀ i, Y i ⟶ X) : @[simp] lemma map_singleton {X Y : C} (f : X ⟶ Y) : (singleton f).map F = singleton (F.map f) := by - rw [← ofArrows_pUnit.{_, _, 0}, map_ofArrows, ofArrows_pUnit] + rw [← ofArrows_pUnit.{0}, map_ofArrows, ofArrows_pUnit] lemma map_le_iff_le_functorPullback {R : Presieve X} {S : Presieve (F.obj X)} : R.map F ≤ S ↔ R ≤ S.functorPullback F := @@ -462,7 +538,8 @@ def uncurry : Set (Σ Y, Y ⟶ X) := obtain ⟨rfl, h⟩ := h; subst h; constructor @[simp] theorem uncurry_pullbackArrows [HasPullbacks C] {B : C} (b : B ⟶ X) : - (pullbackArrows b s).uncurry = (fun f ↦ ⟨pullback f.2 b, pullback.snd _ _⟩) '' s.uncurry := by + (pullbackArrows b s).uncurry = + (fun f ↦ ⟨Limits.pullback f.2 b, pullback.snd _ _⟩) '' s.uncurry := by ext ⟨Z, v⟩; constructor · rintro ⟨Y, u, hu⟩; exact ⟨⟨Y, u⟩, hu, rfl⟩ · rintro ⟨⟨Y, u⟩, hu, h⟩ @@ -956,6 +1033,19 @@ theorem pullbackArrows_comm {X Y : C} (f : Y ⟶ X) (R : Presieve X) [R.HasPullb have := R.hasPullback f hk exact ⟨_, _, _, Presieve.pullbackArrows.mk _ _ hk, pullback.lift_snd _ _ comm⟩ +lemma pullback_arrows {X Y : C} (f : X ⟶ Y) (S : Sieve Y) : + (S.pullback f).arrows = S.arrows.pullback f := + rfl + +lemma pushforward_arrows {X Y : C} (f : X ⟶ Y) (S : Sieve X) : + (S.pushforward f).arrows = S.arrows.pushforward f := + rfl + +lemma generate_pushforward {X Y : C} (f : X ⟶ Y) (R : Presieve X) : + generate (R.pushforward f) = (generate R).pushforward f := by + ext + grind [generate_apply, Presieve.pushforward, pushforward_apply] + section Functor variable {E : Type u₃} [Category.{v₃} E] (G : D ⥤ E) @@ -1350,7 +1440,7 @@ def shrinkFunctorUliftFunctorIso [LocallySmall.{w} C] [LocallySmall.{max w' w} C fun {U V} f ↦ by dsimp ext - dsimp [Equiv.subtypeEquiv] + dsimp [Equiv.subtypeEquiv_apply] rw [shrinkYonedaObjObjEquiv_obj_map, shrinkYonedaObjObjEquiv_symm_comp] simp @@ -1369,7 +1459,7 @@ variable (S) in noncomputable def shrinkFunctorIsoFunctor : (shrinkFunctor.{v₁} S).toFunctor ≅ S.functor := NatIso.ofComponents (fun Y ↦ Equiv.toIso <| Equiv.subtypeEquiv shrinkYonedaObjObjEquiv (by simp)) fun {U V} f ↦ by - dsimp [Equiv.subtypeEquiv] + dsimp [Equiv.subtypeEquiv_apply] ext simp [shrinkYonedaObjObjEquiv_obj_map] diff --git a/Mathlib/CategoryTheory/Sites/Spaces.lean b/Mathlib/CategoryTheory/Sites/Spaces.lean index 9f424a1beef31f..637268ca43dd1b 100644 --- a/Mathlib/CategoryTheory/Sites/Spaces.lean +++ b/Mathlib/CategoryTheory/Sites/Spaces.lean @@ -5,10 +5,10 @@ Authors: Bhavik Mehta -/ module -public import Mathlib.CategoryTheory.Sites.Grothendieck +public import Mathlib.CategoryTheory.Sites.CoversTop.Basic public import Mathlib.CategoryTheory.Sites.Pretopology public import Mathlib.CategoryTheory.Limits.Lattice -public import Mathlib.Topology.Sets.Opens +public import Mathlib.Topology.Sets.OpenCover /-! # Grothendieck topology on a topological space @@ -57,6 +57,9 @@ def grothendieckTopology : GrothendieckTopology (Opens T) where rcases hR hf _ hU with ⟨V, g, hg, hV⟩ exact ⟨_, g ≫ f, hg, hV⟩ +lemma mem_grothendieckTopology {U : Opens T} {S : Sieve U} : + S ∈ Opens.grothendieckTopology T U ↔ ∀ x ∈ U, ∃ (V : _) (f : V ⟶ U), S f ∧ x ∈ V := .rfl + /-- The Grothendieck pretopology associated to a topological space. -/ def pretopology : Pretopology (Opens T) where coverings X := {R | ∀ x ∈ X, ∃ (U : _) (f : U ⟶ X), R f ∧ x ∈ U} @@ -93,4 +96,16 @@ theorem pretopology_toGrothendieck : rw [← toPretopology_grothendieckTopology] apply (Pretopology.gi (Opens T)).l_u_eq +lemma coversTop_iff {ι : Type*} (U : ι → Opens T) : + (grothendieckTopology T).CoversTop U ↔ IsOpenCover U := by + rw [GrothendieckTopology.coversTop_iff_of_isTerminal _ ⊤ isTerminalTop] + dsimp [Opens.grothendieckTopology] + simp only [IsOpenCover, eq_top_iff, SetLike.le_def, exists_and_right, Opens.mem_top, + Opens.mem_iSup, forall_const] + refine ⟨fun h x ↦ ?_, fun hU x hx ↦ ?_⟩ + · obtain ⟨V, ⟨u, ⟨i, ⟨hi⟩⟩⟩, hx⟩ := h x trivial + use i, leOfHom hi hx + · obtain ⟨i, hi⟩ := hU (x := x) + exact ⟨U i, ⟨homOfLE le_top, ⟨i, ⟨𝟙 _⟩⟩⟩, hi⟩ + end Opens diff --git a/Mathlib/CategoryTheory/Subobject/Classifier/Defs.lean b/Mathlib/CategoryTheory/Subobject/Classifier/Defs.lean index ce1187d910572b..bce43342532765 100644 --- a/Mathlib/CategoryTheory/Subobject/Classifier/Defs.lean +++ b/Mathlib/CategoryTheory/Subobject/Classifier/Defs.lean @@ -392,6 +392,7 @@ namespace SubobjectRepresentableBy given `h : SubobjectRepresentableBy Ω`. -/ def Ω₀ : Subobject Ω := h.homEquiv (𝟙 Ω) +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.Ω₀ := Ω₀ @[deprecated (since := "2026-03-06")] @@ -403,6 +404,7 @@ lemma homEquiv_eq {X : C} (f : X ⟶ Ω) : h.homEquiv f = (Subobject.pullback f).obj h.Ω₀ := by simpa using! h.homEquiv_comp f (𝟙 _) +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.homEquiv_eq := homEquiv_eq @[deprecated (since := "2026-03-06")] @@ -414,6 +416,7 @@ lemma pullback_homEquiv_symm_obj_Ω₀ {X : C} (x : Subobject X) : (Subobject.pullback (h.homEquiv.symm x)).obj h.Ω₀ = x := by rw [← homEquiv_eq, Equiv.apply_symm_apply] +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.pullback_homEquiv_symm_obj_Ω₀ := pullback_homEquiv_symm_obj_Ω₀ @@ -428,6 +431,7 @@ variable {U X : C} (m : U ⟶ X) [Mono m] /-- `h.χ m` is the characteristic map of monomorphism `m` given by the bijection `h.homEquiv`. -/ def χ : X ⟶ Ω := h.homEquiv.symm (Subobject.mk m) +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.χ := χ @[deprecated (since := "2026-03-06")] @@ -440,6 +444,7 @@ noncomputable def iso : MonoOver.mk m ≅ (Subobject.representativeIso (.mk m)).symm ≪≫ Subobject.representative.mapIso (eqToIso (h.pullback_homEquiv_symm_obj_Ω₀ (.mk m)).symm) +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.iso := iso @[deprecated (since := "2026-03-06")] @@ -459,6 +464,7 @@ alias _root_.CategoryTheory.Classifier.SubobjectRepresentableBy.iso := iso noncomputable def π : U ⟶ Subobject.underlying.obj h.Ω₀ := (h.iso m).hom.hom.left ≫ Subobject.pullbackπ (h.χ m) h.Ω₀ +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.π := π @[deprecated (since := "2026-03-06")] @@ -473,6 +479,7 @@ lemma iso_inv_left_π : convert! Category.id_comp _ using 2 exact (MonoOver.forget _ ⋙ Over.forget _).congr_map (h.iso m).inv_hom_id +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.iso_inv_left_π := iso_inv_left_π @[deprecated (since := "2026-03-06")] @@ -484,6 +491,7 @@ lemma iso_inv_hom_left_comp : ((Subobject.pullback (h.χ m)).obj h.Ω₀).arrow := MonoOver.w (h.iso m).inv +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.iso_inv_hom_left_comp := iso_inv_hom_left_comp @@ -491,6 +499,7 @@ alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.iso_inv_hom_left_ alias _root_.CategoryTheory.Classifier.SubobjectRepresentableBy.iso_inv_hom_left_comp := iso_inv_hom_left_comp +set_option linter.dupNamespace false in @[deprecated (since := "2025-12-18")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.iso_inv_left_comp := iso_inv_hom_left_comp @@ -503,6 +512,7 @@ lemma isPullback {U X : C} (m : U ⟶ X) [Mono m] : (Iso.refl _) (Iso.refl _) all_goals simp [MonoOver.forget] +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.isPullback := isPullback @[deprecated (since := "2026-03-06")] @@ -515,6 +525,7 @@ lemma uniq {χ' : X ⟶ Ω} {π : U ⟶ h.Ω₀} simp only [χ, Equiv.apply_symm_apply, homEquiv_eq] simpa using! Subobject.pullback_obj_mk sq.flip +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.uniq := uniq @[deprecated (since := "2026-03-06")] @@ -532,6 +543,7 @@ noncomputable def isTerminalΩ₀ : IsTerminal (h.Ω₀ : C) := rw [← cancel_mono h.Ω₀.arrow, h.uniq this, ← (h.isPullback (𝟙 X)).w, Category.id_comp]) +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.isTerminalΩ₀ := isTerminalΩ₀ @[deprecated (since := "2026-03-06")] @@ -540,6 +552,7 @@ alias _root_.CategoryTheory.Classifier.SubobjectRepresentableBy.isTerminalΩ₀ /-- The unique map to the terminal object. -/ noncomputable def χ₀ (U : C) : U ⟶ h.Ω₀ := h.isTerminalΩ₀.from U +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.χ₀ := χ₀ @[deprecated (since := "2026-03-06")] @@ -548,6 +561,7 @@ alias _root_.CategoryTheory.Classifier.SubobjectRepresentableBy.χ₀ := χ₀ include h in lemma hasTerminal : HasTerminal C := h.isTerminalΩ₀.hasTerminal +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.hasTerminal := hasTerminal @[deprecated (since := "2026-03-06")] @@ -559,6 +573,7 @@ variable [HasTerminal C] noncomputable def isoΩ₀ : (h.Ω₀ : C) ≅ ⊤_ C := h.isTerminalΩ₀.conePointUniqueUpToIso (limit.isLimit _) +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.isoΩ₀ := isoΩ₀ @[deprecated (since := "2026-03-06")] @@ -582,6 +597,7 @@ noncomputable def classifier : Subobject.Classifier C where (by simp) (h.isTerminalΩ₀.hom_ext _ _) (by simp) (by simp) exact h.uniq this +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-06")] alias _root.CategoryTheory.Classifier.SubobjectRepresentableBy.classifier := classifier @[deprecated (since := "2026-03-06")] diff --git a/Mathlib/CategoryTheory/Subpresheaf/Basic.lean b/Mathlib/CategoryTheory/Subpresheaf/Basic.lean deleted file mode 100644 index 7b11436e10dd84..00000000000000 --- a/Mathlib/CategoryTheory/Subpresheaf/Basic.lean +++ /dev/null @@ -1,8 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.CategoryTheory.Category.Init -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/CategoryTheory/Subpresheaf/Equalizer.lean b/Mathlib/CategoryTheory/Subpresheaf/Equalizer.lean deleted file mode 100644 index 43d021ec127875..00000000000000 --- a/Mathlib/CategoryTheory/Subpresheaf/Equalizer.lean +++ /dev/null @@ -1,10 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.CategoryTheory.Category.Init -public import Mathlib.Data.Finset.Attr -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Tactic.SetLike -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/CategoryTheory/Subpresheaf/Finite.lean b/Mathlib/CategoryTheory/Subpresheaf/Finite.lean deleted file mode 100644 index 43d021ec127875..00000000000000 --- a/Mathlib/CategoryTheory/Subpresheaf/Finite.lean +++ /dev/null @@ -1,10 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.CategoryTheory.Category.Init -public import Mathlib.Data.Finset.Attr -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Tactic.SetLike -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/CategoryTheory/Subpresheaf/Image.lean b/Mathlib/CategoryTheory/Subpresheaf/Image.lean deleted file mode 100644 index 43d021ec127875..00000000000000 --- a/Mathlib/CategoryTheory/Subpresheaf/Image.lean +++ /dev/null @@ -1,10 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.CategoryTheory.Category.Init -public import Mathlib.Data.Finset.Attr -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Tactic.SetLike -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/CategoryTheory/Subpresheaf/OfSection.lean b/Mathlib/CategoryTheory/Subpresheaf/OfSection.lean deleted file mode 100644 index 43d021ec127875..00000000000000 --- a/Mathlib/CategoryTheory/Subpresheaf/OfSection.lean +++ /dev/null @@ -1,10 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.CategoryTheory.Category.Init -public import Mathlib.Data.Finset.Attr -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Tactic.SetLike -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/CategoryTheory/Subpresheaf/Sieves.lean b/Mathlib/CategoryTheory/Subpresheaf/Sieves.lean deleted file mode 100644 index 43d021ec127875..00000000000000 --- a/Mathlib/CategoryTheory/Subpresheaf/Sieves.lean +++ /dev/null @@ -1,10 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.CategoryTheory.Category.Init -public import Mathlib.Data.Finset.Attr -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Tactic.SetLike -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/CategoryTheory/Subpresheaf/Subobject.lean b/Mathlib/CategoryTheory/Subpresheaf/Subobject.lean deleted file mode 100644 index 43d021ec127875..00000000000000 --- a/Mathlib/CategoryTheory/Subpresheaf/Subobject.lean +++ /dev/null @@ -1,10 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.CategoryTheory.Category.Init -public import Mathlib.Data.Finset.Attr -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Tactic.SetLike -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/CategoryTheory/Thin.lean b/Mathlib/CategoryTheory/Thin.lean index bcdb471330e201..5a37064b6da206 100644 --- a/Mathlib/CategoryTheory/Thin.lean +++ b/Mathlib/CategoryTheory/Thin.lean @@ -9,6 +9,7 @@ public import Mathlib.CategoryTheory.Functor.Category /-! # Thin categories + A thin category (also known as a sparse category) is a category with at most one morphism between each pair of objects. Examples include posets, but also some indexing categories (diagrams) for special shapes of diff --git a/Mathlib/CategoryTheory/Triangulated/Basic.lean b/Mathlib/CategoryTheory/Triangulated/Basic.lean index 59f0b77bf32158..f19199686ad02f 100644 --- a/Mathlib/CategoryTheory/Triangulated/Basic.lean +++ b/Mathlib/CategoryTheory/Triangulated/Basic.lean @@ -289,10 +289,8 @@ instance : AddCommGroup (T₁ ⟶ T₂) where add_comm f g := by ext <;> apply add_comm neg_add_cancel f := by ext <;> apply neg_add_cancel sub_eq_add_neg f g := by ext <;> apply sub_eq_add_neg - nsmul n f := n • f nsmul_zero f := by cat_disch nsmul_succ n f := by ext <;> apply AddMonoid.nsmul_succ - zsmul n f := n • f zsmul_zero' := by cat_disch zsmul_succ' n f := by ext <;> apply SubNegMonoid.zsmul_succ' zsmul_neg' n f := by ext <;> apply SubNegMonoid.zsmul_neg' diff --git a/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean b/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean index d2307a55e1cbaa..07fe07961a8c2f 100644 --- a/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean +++ b/Mathlib/CategoryTheory/Triangulated/TriangleShift.lean @@ -111,7 +111,7 @@ noncomputable def Triangle.shiftFunctorAdd' (a b n : ℤ) (h : a + b = n) : ← dsimp% (CategoryTheory.shiftFunctorAdd' C a b (a + b) rfl).hom.naturality_assoc] simp only [shiftFunctorAdd'_eq_shiftFunctorAdd, Int.negOnePow_add, shiftFunctorComm_hom_app_comp_shift_shiftFunctorAdd_hom_app, add_comm a])) - (by cat_disch) + (by intros; ext <;> simp) set_option backward.defeqAttrib.useBackward true in set_option backward.isDefEq.respectTransparency false in diff --git a/Mathlib/CategoryTheory/Triangulated/WeakKernels.lean b/Mathlib/CategoryTheory/Triangulated/WeakKernels.lean new file mode 100644 index 00000000000000..980bf83c792ae4 --- /dev/null +++ b/Mathlib/CategoryTheory/Triangulated/WeakKernels.lean @@ -0,0 +1,49 @@ +/- +Copyright (c) 2026 Sophie Morel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sophie Morel +-/ +module + +public import Mathlib.CategoryTheory.Triangulated.Pretriangulated +public import Mathlib.CategoryTheory.Limits.WeakLimits.WeakKernels + +/-! +# Weak kernels in pretriangulated categories + +We prove that pretriangulated categories have weak kernels: if `f : X ⟶ Y` is a morphism in +a pretriangulated category and if we complete it to a distinguished triangle +`Z ⟶ X ⟶ Y ⟶ Z⟦1⟧`, then the first morphism `Z ⟶ Y` of that triangle is a weak kernel of `f`. + +TODO: Weak cokernels. +-/ + +@[expose] public section + +noncomputable section + +namespace CategoryTheory.Pretriangulated + +open Limits Category Preadditive Pretriangulated + +variable {C : Type*} [Category* C] [Preadditive C] [HasZeroObject C] [HasShift C ℤ] + [∀ n : ℤ, Functor.Additive (shiftFunctor C n)] [Pretriangulated C] + +/-- If `T` is a distinguished triangle, then `T.mor₁` defines a kernel fork for `T.mor₂`. -/ +def kernelForkOfDistTriangle (T : Triangle C) (dT : T ∈ distTriang C) : + KernelFork T.mor₂ := KernelFork.ofι T.mor₁ (comp_distTriang_mor_zero₁₂ _ dT) + +/-- If `T` is a distinguished triangle, then the kernel fork for `T.mor₂` defined in +`kernelForkOfDistTriangle` is a weak kernel fork. -/ +def isWeakLimitKernelForkOfDistTriangle (T : Triangle C) (dT : T ∈ distTriang C) : + IsWeakLimit (kernelForkOfDistTriangle _ dT) := + Fork.IsWeakLimit.mk' _ + (fun s ↦ ⟨_, (T.coyoneda_exact₂ dT _ (KernelFork.condition s)).choose_spec.symm⟩) + +/-- A pretriangulated category has weak kernels. -/ +instance : HasWeakKernels C where + hasWeakLimit f := ⟨by + obtain ⟨K, i, p, h⟩ := distinguished_cocone_triangle₁ f + exact ⟨_, isWeakLimitKernelForkOfDistTriangle _ h⟩⟩ + +end CategoryTheory.Pretriangulated diff --git a/Mathlib/Combinatorics/Colex.lean b/Mathlib/Combinatorics/Colex.lean index 638979cbff807d..6daa6f3a5eb2f8 100644 --- a/Mathlib/Combinatorics/Colex.lean +++ b/Mathlib/Combinatorics/Colex.lean @@ -69,8 +69,6 @@ namespace Finset open Colex -instance : Inhabited (Colex (Finset α)) := ⟨toColex ∅⟩ - namespace Colex section PartialOrder variable [PartialOrder α] [PartialOrder β] {f : α → β} {𝒜 𝒜₁ 𝒜₂ : Finset (Finset α)} diff --git a/Mathlib/Combinatorics/Configuration.lean b/Mathlib/Combinatorics/Configuration.lean index 2b1f5a6ad5d985..051a7cb6b3de49 100644 --- a/Mathlib/Combinatorics/Configuration.lean +++ b/Mathlib/Combinatorics/Configuration.lean @@ -11,6 +11,7 @@ public import Mathlib.LinearAlgebra.Projectivization.Constructions /-! # Configurations of Points and lines + This file introduces abstract configurations of points and lines, and proves some basic properties. ## Main definitions diff --git a/Mathlib/Combinatorics/Enumerative/Bell.lean b/Mathlib/Combinatorics/Enumerative/Bell.lean index 00f5a2ddd4ba94..b4a6ea99c6b401 100644 --- a/Mathlib/Combinatorics/Enumerative/Bell.lean +++ b/Mathlib/Combinatorics/Enumerative/Bell.lean @@ -1,10 +1,12 @@ /- Copyright (c) 2024 Antoine Chambert-Loir & María-Inés de Frutos—Fernández. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: Antoine Chambert-Loir, María-Inés de Frutos—Fernández, Yu Shao, Beibei Xiong, Weijie Jiang +Authors: Antoine Chambert-Loir, María-Inés de Frutos—Fernández, Yu Shao, Beibei Xiong, Weijie Jiang, +Yi Yuan -/ module +public import Mathlib.Combinatorics.Enumerative.Partition.Basic public import Mathlib.Data.Nat.Choose.Multinomial /-! # Bell numbers for multisets @@ -74,15 +76,12 @@ private theorem bell_mul_eq_lemma {x : ℕ} (hx : x ≠ 0) : ring_nf theorem bell_mul_eq (m : Multiset ℕ) : - m.bell * (m.map (fun j ↦ j !)).prod * ∏ j ∈ (m.toFinset.erase 0), (m.count j)! - = m.sum ! := by + m.bell * (m.map (fun j ↦ j !)).prod * ∏ j ∈ (m.toFinset.erase 0), (m.count j)! = m.sum ! := by unfold bell rw [← Nat.mul_right_inj (a := ∏ i ∈ m.toFinset, (i * count i m)!) (by positivity)] - simp only [← mul_assoc] - rw [Nat.multinomial_spec] - simp only [mul_assoc] - rw [mul_comm] - apply congr_arg₂ + simp only [← mul_assoc, Nat.multinomial_spec] + rw [mul_assoc, mul_assoc, mul_comm] + congr · rw [mul_comm, mul_assoc, ← Finset.prod_mul_distrib, Finset.prod_multiset_map_count] suffices this : _ by by_cases hm : 0 ∈ m.toFinset @@ -94,30 +93,50 @@ theorem bell_mul_eq (m : Multiset ℕ) : nth_rewrite 3 [← Finset.erase_eq_of_notMem hm] exact this rw [← Finset.prod_mul_distrib] - apply Finset.prod_congr rfl - intro x hx + congr! 1 with x hx rw [← mul_assoc, bell_mul_eq_lemma] simp only [Finset.mem_erase, ne_eq, mem_toFinset] at hx simp only [ne_eq, hx.1, not_false_eq_true] - · apply congr_arg - rw [Finset.sum_multiset_count] + · rw [Finset.sum_multiset_count] simp only [smul_eq_mul, mul_comm] theorem bell_eq (m : Multiset ℕ) : - m.bell = m.sum ! / ((m.map (fun j ↦ j !)).prod * - ∏ j ∈ (m.toFinset.erase 0), (m.count j)!) := by + m.bell = m.sum ! / ((m.map fun j ↦ j !).prod * ∏ j ∈ m.toFinset.erase 0, (m.count j)!) := by rw [← Nat.mul_left_inj, Nat.div_mul_cancel _] · rw [← mul_assoc] exact bell_mul_eq m · rw [← bell_mul_eq, mul_assoc] apply Nat.dvd_mul_left · rw [← Nat.pos_iff_ne_zero] - apply Nat.mul_pos - · simp only [CanonicallyOrderedAdd.multiset_prod_pos, mem_map, forall_exists_index, and_imp, - forall_apply_eq_imp_iff₂] - exact fun _ _ ↦ Nat.factorial_pos _ - · apply Finset.prod_pos - exact fun _ _ ↦ Nat.factorial_pos _ + exact Nat.mul_pos (by simp [Nat.factorial_pos]) (by positivity) + +private theorem bell_cons_mul_count (m : Multiset ℕ) {a : ℕ} (ha : a ≠ 0) : + (a ::ₘ m).bell * (a ::ₘ m).count a = (m.sum + a).choose a * m.bell := by + let rest := ∏ j ∈ (m.toFinset.erase 0).erase a, (m.count j)! + have hrest : rest = ∏ j ∈ ((a ::ₘ m).toFinset.erase 0).erase a, ((a ::ₘ m).count j)! := by + unfold rest + congr! 1 with j hj + · grind [Multiset.toFinset_cons] + · simp [Finset.mem_erase.mp hj] + let c := (m.map (· !)).prod * (m.count a)! * rest + have hm0 : m.bell * c = m.sum ! := by + have hsplit : (m.count a)! * rest = ∏ j ∈ m.toFinset.erase 0, (m.count j)! := by + by_cases hmem : a ∈ m.toFinset.erase 0 + · rw [← Finset.mul_prod_erase _ _ hmem] + · have hcount : m.count a = 0 := by grind [Multiset.count_eq_zero_of_notMem] + simp [rest, Finset.erase_eq_of_notMem hmem, hcount] + simpa [c, hsplit, mul_assoc] using Multiset.bell_mul_eq m + have hm : m.sum ! * a ! = m.bell * a ! * c := by grind + have hc : 0 < a ! * c := Nat.mul_pos (by positivity) <| + Nat.mul_pos (by simp [Nat.factorial_pos]) (by positivity) + apply Nat.eq_of_mul_eq_mul_right hc + calc + _ = (m.sum + a)! := by + have hq := Multiset.bell_mul_eq (a ::ₘ m) + rw [← Finset.mul_prod_erase _ _ (a := a) (by simp [*]), ← hrest] at hq + simpa [c, Nat.factorial_succ, add_comm, mul_assoc, mul_left_comm] using hq + _ = ((m.sum + a).choose a * m.bell) * (a ! * c) := by + simp [← Nat.add_choose_mul_factorial_mul_factorial, mul_assoc, hm] end Multiset @@ -128,7 +147,7 @@ of `n`-element subsets. -/ def uniformBell (m n : ℕ) : ℕ := bell (replicate m n) theorem uniformBell_eq (m n : ℕ) : m.uniformBell n = - ∏ p ∈ (Finset.range m), Nat.choose (p * n + n - 1) (n - 1) := by + ∏ p ∈ (Finset.range m), choose (p * n + n - 1) (n - 1) := by unfold uniformBell bell rw [toFinset_replicate] split_ifs with hm @@ -172,32 +191,26 @@ theorem uniformBell_eq_div (m : ℕ) {n : ℕ} (hn : n ≠ 0) : uniformBell m n = (m * n)! / (n ! ^ m * m !) := by rw [eq_comm] apply Nat.div_eq_of_eq_mul_left - · exact Nat.mul_pos (Nat.pow_pos (Nat.factorial_pos n)) m.factorial_pos + · exact Nat.mul_pos (Nat.pow_pos n.factorial_pos) m.factorial_pos · rw [← mul_assoc, ← uniformBell_mul_eq _ hn] /-- The `n`th standard Bell number, which counts the number of partitions of a set of cardinality `n`. - -## TODO - -Prove that `Nat.bell n` is equal to the sum of `Multiset.bell m` -over all multisets `m : Multiset ℕ` such that `m.sum = n`. -/ protected def bell : ℕ → ℕ | 0 => 1 - | n + 1 => ∑ i : Fin n.succ, choose n i * Nat.bell (n - i) + | n + 1 => ∑ i ≤ n, choose n i * (n - i).bell theorem bell_succ (n : ℕ) : - Nat.bell (n + 1) = ∑ i : Fin n.succ, Nat.choose n i * Nat.bell (n - i) := by + (n + 1).bell = ∑ i ≤ n, choose n i * (n - i).bell := by rw [Nat.bell] theorem bell_succ' (n : ℕ) : - Nat.bell (n + 1) = ∑ ij ∈ Finset.antidiagonal n, Nat.choose n ij.1 * Nat.bell ij.2 := by + (n + 1).bell = ∑ ij ∈ Finset.antidiagonal n, choose n ij.1 * ij.2.bell := by rw [Nat.bell_succ, - Finset.Nat.sum_antidiagonal_eq_sum_range_succ (fun x y => Nat.choose n x * Nat.bell y) n, - Finset.sum_range] - + ← Nat.range_succ_eq_Iic, + ← Finset.Nat.sum_antidiagonal_eq_sum_range_succ (fun x y ↦ choose n x * y.bell) n] @[simp] theorem bell_zero : Nat.bell 0 = 1 := by @@ -205,10 +218,69 @@ theorem bell_zero : Nat.bell 0 = 1 := by @[simp] theorem bell_one : Nat.bell 1 = 1 := by - simp [Nat.bell] + have : Finset.Iic 0 = {0} := Eq.symm (Finset.eq_of_veq rfl) + simp [Nat.bell, this] @[simp] theorem bell_two : Nat.bell 2 = 2 := by - simp [Nat.bell] + have : Finset.Iic 1 = {0, 1} := Finset.eq_of_veq rfl + simp [Nat.bell, this] + +theorem bell_eq_sum_erase {n : ℕ} (p : (n + 1).Partition) : + p.parts.bell = ∑ a ∈ p.parts.toFinset, n.choose (a - 1) * (p.parts.erase a).bell := by + apply Nat.eq_of_mul_eq_mul_left n.succ_pos + calc + _ = (∑ a ∈ p.parts.toFinset, p.parts.count a * a) * p.parts.bell := by + rw [succ_eq_add_one, mul_eq_mul_right_iff] + left + simpa [smul_eq_mul, p.parts_sum] using Finset.sum_multiset_count p.parts + _ = ∑ a ∈ p.parts.toFinset, a * (p.parts.count a * p.parts.bell) := by grind [Finset.sum_mul] + _ = ∑ a ∈ p.parts.toFinset, (n + 1) * (n.choose (a - 1) * (p.parts.erase a).bell) := by + congr! 1 with a ha + have ha0 : a ≠ 0 := by grind + have hsum : (p.parts.erase a).sum + a = n + 1 := by + simpa [p.parts_sum, add_comm] using congrArg Multiset.sum (cons_erase (mem_dedup.mp ha)) + grind [Nat.add_one_mul_choose_eq, cons_erase, bell_cons_mul_count] + _ = _ := by rw [Finset.mul_sum] + +private def sigmaPartitionWithPartEquiv (n : ℕ) : + (Σ i : Fin n.succ, {p : (n + 1).Partition // (i + 1 : ℕ) ∈ p.parts}) ≃ + Σ p : (n + 1).Partition, {a : ℕ // a ∈ p.parts.toFinset} where + toFun x := ⟨x.2.1, ⟨(x.1 + 1 : ℕ), by simpa using x.2.2⟩⟩ + invFun x := ⟨⟨x.2.1 - 1, by grind⟩, ⟨x.1, by grind⟩⟩ + left_inv x := by simp + right_inv x := by grind + +/-- `Nat.bell n` is equal to the sum of `Multiset.bell m` over all multisets `m : Multiset ℕ` such +that `m.sum = n`. -/ +theorem bell_eq_sum_partition (n : ℕ) : n.bell = ∑ p : n.Partition, p.parts.bell := by + refine Nat.strong_induction_on n ?_ + rintro (_ | n) ih + · simp + rw [Nat.bell_succ] + calc + _ = ∑ i ≤ n, ∑ q : (n - i).Partition, n.choose i * q.parts.bell := by + congr! with i + simp [ih (n - i) _, Finset.mul_sum] + _ = ∑ i ≤ n, ∑ p : {p : (n + 1).Partition // (i + 1 : ℕ) ∈ p.parts}, + choose n i * (p.1.parts.erase (i + 1)).bell := by + congr! with i hi + have : i ≤ n := Finset.mem_Iic.mp hi + have h1 : 1 ≤ (i + 1 : ℕ) := by lia + have h2 : (i + 1 : ℕ) ≤ n + 1 := by lia + have hsub : n + 1 - (i + 1 : ℕ) = n - i := by lia + exact hsub ▸ (Fintype.sum_equiv (Partition.partitionWithPartEquiv h1 h2) _ _ (fun _ ↦ rfl)).symm + _ = ∑ x : Σ p : (n + 1).Partition, p.parts.toFinset, + choose n (x.2.1 - 1) * (x.1.parts.erase x.2.1).bell := by + rw [← Nat.range_succ_eq_Iic, Finset.sum_range, ← Fintype.sum_sigma'] + refine Fintype.sum_equiv (sigmaPartitionWithPartEquiv n) _ _ ?_ + simp [sigmaPartitionWithPartEquiv] + _ = ∑ p : (n + 1).Partition, ∑ a : p.parts.toFinset, choose n (a - 1) * (p.parts.erase a).bell := + Fintype.sum_sigma' fun (p : (n + 1).Partition) (a : p.parts.toFinset) ↦ + choose n (a - 1) * (p.parts.erase a.1).bell + _ = _ := by + congr! with p + rw [bell_eq_sum_erase p] + exact p.parts.toFinset.sum_coe_sort (fun a ↦ choose n (a - 1) * (p.parts.erase a).bell) end Nat diff --git a/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean b/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean index c29da1292184a8..007aa07a4cbc53 100644 --- a/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean +++ b/Mathlib/Combinatorics/Enumerative/Catalan/Tree.lean @@ -26,7 +26,7 @@ import Mathlib.Tactic.Field open Finset -open Finset.antidiagonal (fst_le snd_le) +open Finset.HasAntidiagonal.antidiagonal (fst_le snd_le) namespace BinaryTree diff --git a/Mathlib/Combinatorics/Enumerative/DoubleCounting.lean b/Mathlib/Combinatorics/Enumerative/DoubleCounting.lean index dcaf5eec09d35a..cefd3bfb853fe6 100644 --- a/Mathlib/Combinatorics/Enumerative/DoubleCounting.lean +++ b/Mathlib/Combinatorics/Enumerative/DoubleCounting.lean @@ -203,6 +203,16 @@ theorem card_le_card_of_forall_subsingleton' (ht : ∀ b ∈ t, ∃ a, a ∈ s (hs : ∀ a ∈ s, ({ b ∈ t | r a b } : Set β).Subsingleton) : #t ≤ #s := card_le_card_of_forall_subsingleton (swap r) ht hs +/-- Given a finite collection of finite subsets $B_1, \ldots, B_k$ +and, for every $x \in \bigcup_i B_i$, let $C_x$ be the set of indices +of the $B_i$'s that contain $x$. Then, $\sum_i |B_i| = \sum_x |C_x|$. -/ +lemma sum_card_eq_sum_biUnion_card [Fintype α] [DecidableEq α] [DecidableEq β] + (B : α → Finset β) (s : Finset α) : + ∑ j ∈ s, #(B j) = ∑ x ∈ s.biUnion B, #{j | j ∈ s ∧ x ∈ B j} := by + convert sum_card_bipartiteAbove_eq_sum_card_bipartiteBelow (fun j x => x ∈ B j) + · grind [bipartiteAbove] + · grind [bipartiteBelow] + end Bipartite end Finset diff --git a/Mathlib/Combinatorics/Enumerative/DyckWord.lean b/Mathlib/Combinatorics/Enumerative/DyckWord.lean index 0dfcee6466b377..b4462eda3c50f6 100644 --- a/Mathlib/Combinatorics/Enumerative/DyckWord.lean +++ b/Mathlib/Combinatorics/Enumerative/DyckWord.lean @@ -557,8 +557,8 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: `p.firstReturn` is positive if `p` is nonzero. -/ @[positivity DyckWord.firstReturn _] -meta def evalDyckWordFirstReturn : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalDyckWordFirstReturn : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(DyckWord.firstReturn $a) => assertInstancesCommute diff --git a/Mathlib/Combinatorics/Enumerative/Partition/Basic.lean b/Mathlib/Combinatorics/Enumerative/Partition/Basic.lean index 4bb7639e6fa7b9..935de73ea4c4a9 100644 --- a/Mathlib/Combinatorics/Enumerative/Partition/Basic.lean +++ b/Mathlib/Combinatorics/Enumerative/Partition/Basic.lean @@ -229,6 +229,33 @@ theorem countRestricted_two (n : ℕ) : countRestricted n 2 = distincts n := by def oddDistincts (n : ℕ) : Finset n.Partition := odds n ∩ distincts n +/-- If `1 ≤ a` and `a ≤ n`, partitions of `n` containing `a` as a part are equivalent to +partitions of `n - a`. The forward map removes one occurrence of `a`, and the inverse adds `a` as +a part. -/ +def partitionWithPartEquiv {n a : ℕ} (ha1 : 1 ≤ a) (ha : a ≤ n) : + {p : n.Partition // a ∈ p.parts} ≃ (n - a).Partition where + toFun p := by + refine ⟨p.1.parts.erase a, ?_, ?_⟩ + · intro _ hi + exact p.1.parts_pos (p.1.parts.erase_subset a hi) + · have hs : a + (p.1.parts.erase a).sum = n := by + simpa [p.1.parts_sum] using congrArg Multiset.sum (Multiset.cons_erase p.2) + lia + invFun q := ⟨⟨a ::ₘ q.parts, by grind, by simp [q.parts_sum, ha]⟩, by simp⟩ + left_inv p := Subtype.ext <| Partition.ext <| cons_erase p.property + right_inv q := Partition.ext <| erase_cons_head a q.parts + +@[simp] +theorem partitionWithPartEquiv_apply_parts {n a : ℕ} (ha1 : 1 ≤ a) (ha : a ≤ n) + (p : {p : n.Partition // a ∈ p.parts}) : + (partitionWithPartEquiv ha1 ha p).parts = p.1.parts.erase a := by + dsimp [partitionWithPartEquiv] + +@[simp] +theorem partitionWithPartEquiv_symm_apply_parts {n a : ℕ} (ha1 : 1 ≤ a) (ha : a ≤ n) + (p : (n - a).Partition) : ((partitionWithPartEquiv ha1 ha).symm p).1.parts = a ::ₘ p.parts := by + dsimp [partitionWithPartEquiv] + end Partition end Nat diff --git a/Mathlib/Combinatorics/Graph/Subgraph.lean b/Mathlib/Combinatorics/Graph/Subgraph.lean index e95c7da66199bc..720b5263a920f6 100644 --- a/Mathlib/Combinatorics/Graph/Subgraph.lean +++ b/Mathlib/Combinatorics/Graph/Subgraph.lean @@ -46,7 +46,7 @@ graphs, subgraph, induced subgraph, spanning subgraph, closed subgraph public section variable {α β : Type*} {x y z u v w : α} {e f : β} {G G₁ G₂ H H₁ H₂ K : Graph α β} {F F₁ F₂ : Set β} - {X Y : Set α} + {X Y : Set α} open Set diff --git a/Mathlib/Combinatorics/Hindman.lean b/Mathlib/Combinatorics/Hindman.lean index 66f54aeee73816..1faeeab5012304 100644 --- a/Mathlib/Combinatorics/Hindman.lean +++ b/Mathlib/Combinatorics/Hindman.lean @@ -104,16 +104,20 @@ section Aliases we provide match patterns that preserve the defeq correctly in their type. -/ variable {M} [Semigroup M] (a : Stream' M) (m : M) (h : FP a.tail m) + +set_option linter.defProp false in /-- Constructor for `FP`. This is the preferred spelling over `FP.head'`. -/ -@[to_additive (attr := match_pattern, nolint defLemma) +@[to_additive (attr := match_pattern) /-- Constructor for `FS`. This is the preferred spelling over `FS.head'`. -/] abbrev FP.head : a.head ∈ FP a := FP.head' a +set_option linter.defProp false in /-- Constructor for `FP`. This is the preferred spelling over `FP.tail'`. -/ -@[to_additive (attr := match_pattern, nolint defLemma) +@[to_additive (attr := match_pattern) /-- Constructor for `FS`. This is the preferred spelling over `FS.tail'`. -/] abbrev FP.tail : m ∈ FP a := FP.tail' a m h +set_option linter.defProp false in /-- Constructor for `FP`. This is the preferred spelling over `FP.cons'`. -/ -@[to_additive (attr := match_pattern, nolint defLemma) +@[to_additive (attr := match_pattern) /-- Constructor for `FS`. This is the preferred spelling over `FS.cons'`. -/] abbrev FP.cons : a.head * m ∈ FP a := FP.cons' a m h diff --git a/Mathlib/Combinatorics/Pigeonhole.lean b/Mathlib/Combinatorics/Pigeonhole.lean index f76c77f931577d..8c2b16d3c1cbe9 100644 --- a/Mathlib/Combinatorics/Pigeonhole.lean +++ b/Mathlib/Combinatorics/Pigeonhole.lean @@ -11,6 +11,8 @@ public import Mathlib.Data.Nat.ModEq public import Mathlib.Order.Preorder.Finite public import Mathlib.Algebra.Order.BigOperators.Group.Finset +import Mathlib.Combinatorics.Enumerative.DoubleCounting + /-! # Pigeonhole principles @@ -45,6 +47,8 @@ The versions vary by: (`∀ x ∈ s, f x ∈ t`), or assume that for `y ∉ t`, the total weight of the pigeons in this pigeonhole `∑ x ∈ s with f x = y, w x` is nonpositive or nonnegative depending on the inequality we are proving. +* in the case where the "holes" are not necessarily disjoint, that is, a pigeon could be in multiple + holes at the same time, a set-valued version is provided. Lemma names follow `mathlib` convention (e.g., `Finset.exists_lt_sum_fiber_of_maps_to_of_nsmul_lt_sum`); "pigeonhole principle" is mentioned in the @@ -288,6 +292,31 @@ theorem exists_card_fiber_le_of_card_le_mul (ht : t.Nonempty) (hn : #s ≤ #t * ∃ y ∈ t, #{x ∈ s | f x = y} ≤ n := exists_card_fiber_le_of_card_le_nsmul ht hn +/-- A version of the pigeonhole principle for set-valued functions. + +Given a family of sets `f : α → Finset β` and a choice of indices `s : Finset α`. +Let `k` denote the minimum cardinality of the `f j`s. +If the cardinality of the union `s.biUnion f` is less than `s.card`, then +there exists an element `x ∈ s.biUnion f` which is covered by more than `k` of the sets +`f j` (i.e., `k < #{j ∈ s | x ∈ f j}`). + +This is a double-counting variant of the pigeonhole principle. +Unlike the classical pigeonhole principle (see +`Finset.exists_lt_card_fiber_of_nsmul_lt_card_of_maps_to`), +this formulation handles a *set-valued* assignment where elements may belong to +multiple sets simultaneously. -/ +lemma exists_mem_exists_mem_inf'_card_lt [DecidableEq α] [Fintype α] {f : α → Finset β} + (h₁ : s.Nonempty) (h₂ : ∀ j ∈ s, 0 < #(f j)) (h₃ : #(s.biUnion f) < #s) : + ∃ a ∈ s, ∃ x ∈ f a, (s.inf' h₁ fun j ↦ #(f j)) < #{j | j ∈ s ∧ x ∈ f j} := by + set k := s.inf' h₁ (fun j ↦ #(f j)) with hk + contrapose! h₃ + suffices #s • k ≤ #(s.biUnion f) • k by simp_all + simp only [← Finset.sum_const] + calc ∑ j ∈ s, k + _ ≤ ∑ j ∈ s, #(f j) := by gcongr with i hi; exact inf'_le _ hi + _ = ∑ x ∈ s.biUnion f, #{j | j ∈ s ∧ x ∈ f j} := by rw [sum_card_eq_sum_biUnion_card] + _ ≤ ∑ x ∈ s.biUnion f, k := by gcongr; grind + end Finset namespace Fintype diff --git a/Mathlib/Combinatorics/SimpleGraph/Acyclic.lean b/Mathlib/Combinatorics/SimpleGraph/Acyclic.lean index 3e06e3752f8d9f..d345f587840602 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Acyclic.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Acyclic.lean @@ -71,7 +71,7 @@ variable {G G'} /-- A graph that has an injective homomorphism to an acyclic graph is acyclic. -/ lemma IsAcyclic.comap (f : G →g G') (hinj : Function.Injective f) (h : G'.IsAcyclic) : G.IsAcyclic := - fun _ _ ↦ map_isCycle_iff_of_injective hinj |>.not.mp <| h _ + fun _ _ ↦ mt (.map hinj) (h _) lemma IsAcyclic.embedding (f : G ↪g G') (h : G'.IsAcyclic) : G.IsAcyclic := h.comap f f.injective @@ -186,31 +186,8 @@ alias isAcyclic_iff_forall_edge_isBridge := isAcyclic_iff_forall_isBridge theorem IsAcyclic.path_unique {G : SimpleGraph V} (h : G.IsAcyclic) {v w : V} (p q : G.Path v w) : p = q := by - obtain ⟨p, hp⟩ := p - obtain ⟨q, hq⟩ := q - rw [Subtype.mk.injEq] - induction p with - | nil => - exact isPath_iff_nil.mp hq |>.eq_nil.symm - | @cons u v _ ph p ih => - rw [isAcyclic_iff_forall_isBridge] at h - specialize h (e := s(u, v)) (by simpa) - rw [isBridge_iff_forall_walk_mem_edges] at h - replace h := h (q.append p.reverse) - simp only [Walk.edges_append, Walk.edges_reverse, List.mem_append, List.mem_reverse] at h - rcases h with h | h - · cases q with - | nil => simp at hp - | cons _ q => - rw [Walk.cons_isPath_iff] at hp hq - simp only [Walk.edges_cons, List.mem_cons, Sym2.eq_iff, true_and] at h - rcases h with (⟨h, rfl⟩ | ⟨rfl, rfl⟩) | h - · cases ih hp.1 q hq.1 - rfl - · simp at hq - · exact absurd (Walk.fst_mem_support_of_mem_edges _ h) hq.2 - · rw [Walk.cons_isPath_iff] at hp - exact absurd (Walk.fst_mem_support_of_mem_edges _ h) hp.2 + have := p.isPath.exists_isCycle_of_ne q.isPath + grind [IsAcyclic, Subtype.coe_inj] theorem isAcyclic_of_path_unique (h : ∀ (v w : V) (p q : G.Path v w), p = q) : G.IsAcyclic := by intro v c hc diff --git a/Mathlib/Combinatorics/SimpleGraph/Basic.lean b/Mathlib/Combinatorics/SimpleGraph/Basic.lean index d1b86b494d8c74..7f274aa1ba3bcc 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Basic.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Basic.lean @@ -316,6 +316,7 @@ instance completeAtomicBooleanAlgebra : CompleteAtomicBooleanAlgebra (SimpleGrap iInf_iSup_eq f := by ext; simp [Classical.skolem] /-- The complete graph on a type `V` is the simple graph with all pairs of distinct vertices. -/ +@[wikidata Q45715] abbrev completeGraph (V : Type u) : SimpleGraph V := ⊤ /-- The graph with no edges on a given vertex type `V`. -/ @@ -1018,10 +1019,72 @@ theorem Adj.not_isIsolated_right (h : G.Adj u v) : ¬G.IsIsolated v := h.symm.not_isIsolated_left @[simp] -theorem isIsolated_bot : IsIsolated ⊥ v := +protected theorem IsIsolated.bot : IsIsolated ⊥ v := neighborSet_eq_empty _ |>.mp neighborSet_bot +@[deprecated (since := "2026-06-19")] +alias isIsolated_bot := IsIsolated.bot + theorem eq_bot_iff_isIsolated : G = ⊥ ↔ ∀ v, G.IsIsolated v := by simp [eq_bot_iff_forall_not_adj, ← neighborSet_eq_empty, Set.eq_empty_iff_forall_notMem] +section IsUniversal + +variable {G} + +/-- A vertex in a graph is universal if it's adjacent to every other vertex. -/ +def IsUniversal (G : SimpleGraph V) (v : V) : Prop := ∀ ⦃w⦄, v ≠ w → G.Adj v w + +@[simp] lemma insert_neighborSet_eq_univ : + insert v (G.neighborSet v) = Set.univ ↔ G.IsUniversal v := by + simp only [Set.ext_iff, Set.mem_insert_iff, mem_neighborSet, IsUniversal] + grind + +@[simp] lemma neighborSet_eq_compl_singleton : G.neighborSet v = {v}ᶜ ↔ G.IsUniversal v := by + grind [insert_neighborSet_eq_univ, notMem_neighborSet_self] + +protected alias ⟨IsUniversal.of_neighborSet_eq, IsUniversal.neighborSet_eq⟩ := + neighborSet_eq_compl_singleton + +@[simp] +theorem IsUniversal.of_subsingleton [Subsingleton V] : G.IsUniversal v := + fun _ hne ↦ False.elim <| hne (Subsingleton.elim ..) + +theorem IsUniversal.not_isIsolated [Nontrivial V] (h : G.IsUniversal v) (w : V) : + ¬G.IsIsolated w := by + by_cases h' : v = w + · obtain ⟨u, hu⟩ := exists_ne v + exact h' ▸ Adj.not_isIsolated_left (h hu.symm) + · exact Adj.not_isIsolated_right (h h') + +theorem IsIsolated.not_isUniversal [Nontrivial V] (h : G.IsIsolated v) (w : V) : + ¬G.IsUniversal w := by + contrapose! h + exact h.not_isIsolated v + +@[simp] +theorem isUniversal_compl_iff_isIsolated : Gᶜ.IsUniversal v ↔ G.IsIsolated v := by + refine ⟨fun h x hx ↦ ?_, fun h x hx ↦ ?_⟩ + · simpa [hx] using h hx.ne + · simpa [hx] using h x + +alias ⟨IsIsolated.of_isUniversal_compl, _⟩ := isUniversal_compl_iff_isIsolated + +@[simp] +theorem isIsolated_compl_iff_isUniversal : Gᶜ.IsIsolated v ↔ G.IsUniversal v := by + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · simpa using isUniversal_compl_iff_isIsolated.mpr h + · exact isUniversal_compl_iff_isIsolated.mp (by simpa) + +alias ⟨IsUniversal.of_isIsolated_compl, _⟩ := isIsolated_compl_iff_isUniversal + +theorem eq_top_iff_forall_isUniversal : G = ⊤ ↔ ∀ v, G.IsUniversal v := by + simp [eq_top_iff_forall_ne_adj, IsUniversal] + +@[simp] +protected theorem IsUniversal.top : IsUniversal ⊤ v := + eq_top_iff_forall_isUniversal.mp rfl v + +end IsUniversal + end SimpleGraph diff --git a/Mathlib/Combinatorics/SimpleGraph/Circulant.lean b/Mathlib/Combinatorics/SimpleGraph/Circulant.lean index 3ef93ddc16cd5f..e206f4e14024ba 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Circulant.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Circulant.lean @@ -5,9 +5,8 @@ Authors: Iván Renison, Bhavik Mehta -/ module -public import Mathlib.Algebra.Group.Fin.Basic -public import Mathlib.Combinatorics.SimpleGraph.Hasse public import Mathlib.Algebra.Group.Pointwise.Set.Basic +public import Mathlib.Combinatorics.SimpleGraph.CycleGraph /-! # Definition of circulant graphs @@ -19,15 +18,12 @@ are adjacent if and only if `u - v ∈ s` or `v - u ∈ s`. The elements of `s` ## Main declarations * `SimpleGraph.circulantGraph s`: the circulant graph over `G` with jumps `s`. -* `SimpleGraph.cycleGraph n`: the cycle graph over `Fin n`. -/ @[expose] public section namespace SimpleGraph -open Walk - /-- Circulant graph over additive group `G` with jumps `s` -/ @[simps!] def circulantGraph {G : Type*} [AddGroup G] (s : Set G) : SimpleGraph G := @@ -60,150 +56,9 @@ instance [DecidableEq G] [DecidablePred (· ∈ s)] : DecidableRel (circulantGra theorem circulantGraph_adj_translate {s : Set G} {u v d : G} : (circulantGraph s).Adj (u + d) (v + d) ↔ (circulantGraph s).Adj u v := by simp -/-- Cycle graph over `Fin n` -/ -def cycleGraph : (n : ℕ) → SimpleGraph (Fin n) - | 0 | 1 => ⊥ - | _ + 2 => { - Adj a b := a - b = 1 ∨ b - a = 1 - } - -instance : (n : ℕ) → DecidableRel (cycleGraph n).Adj - | 0 | 1 => fun _ _ => inferInstanceAs (Decidable False) - | _ + 2 => by unfold cycleGraph; infer_instance - theorem cycleGraph_eq_circulantGraph (n : ℕ) : cycleGraph (n + 1) = circulantGraph {1} := by cases n · exact edgeFinset_inj.mp rfl · aesop -theorem cycleGraph_zero_adj {u v : Fin 0} : ¬(cycleGraph 0).Adj u v := id - -theorem cycleGraph_zero_eq_bot : cycleGraph 0 = ⊥ := Subsingleton.elim _ _ -theorem cycleGraph_one_eq_bot : cycleGraph 1 = ⊥ := Subsingleton.elim _ _ -theorem cycleGraph_zero_eq_top : cycleGraph 0 = ⊤ := Subsingleton.elim _ _ -theorem cycleGraph_one_eq_top : cycleGraph 1 = ⊤ := Subsingleton.elim _ _ - -theorem cycleGraph_two_eq_top : cycleGraph 2 = ⊤ := by - simp only [SimpleGraph.ext_iff, funext_iff] - decide - -theorem cycleGraph_three_eq_top : cycleGraph 3 = ⊤ := by - simp only [SimpleGraph.ext_iff, funext_iff] - decide - -theorem cycleGraph_one_adj {u v : Fin 1} : ¬(cycleGraph 1).Adj u v := by - simp [cycleGraph_one_eq_bot] - -theorem cycleGraph_adj {n : ℕ} {u v : Fin (n + 2)} : - (cycleGraph (n + 2)).Adj u v ↔ u - v = 1 ∨ v - u = 1 := Iff.rfl - -theorem cycleGraph_adj' {n : ℕ} {u v : Fin n} : - (cycleGraph n).Adj u v ↔ (u - v).val = 1 ∨ (v - u).val = 1 := by - match n with - | 0 => exact u.elim0 - | 1 => simp [cycleGraph_one_adj] - | n + 2 => simp [cycleGraph_adj, Fin.ext_iff] - -theorem cycleGraph_neighborSet {n : ℕ} {v : Fin (n + 2)} : - (cycleGraph (n + 2)).neighborSet v = {v - 1, v + 1} := by - ext w - simp only [mem_neighborSet, Set.mem_insert_iff, Set.mem_singleton_iff] - rw [cycleGraph_adj, sub_eq_iff_eq_add', sub_eq_iff_eq_add', eq_sub_iff_add_eq, eq_comm] - -theorem cycleGraph_neighborFinset {n : ℕ} {v : Fin (n + 2)} : - (cycleGraph (n + 2)).neighborFinset v = {v - 1, v + 1} := by - simp [neighborFinset, cycleGraph_neighborSet] - -theorem cycleGraph_degree_two_le {n : ℕ} {v : Fin (n + 2)} : - (cycleGraph (n + 2)).degree v = Finset.card {v - 1, v + 1} := by - rw [SimpleGraph.degree, cycleGraph_neighborFinset] - -theorem cycleGraph_degree_three_le {n : ℕ} {v : Fin (n + 3)} : - (cycleGraph (n + 3)).degree v = 2 := by - rw [cycleGraph_degree_two_le, Finset.card_pair] - simp only [ne_eq, sub_eq_iff_eq_add, add_assoc v, left_eq_add] - exact ne_of_beq_false rfl - -theorem pathGraph_le_cycleGraph {n : ℕ} : pathGraph n ≤ cycleGraph n := by - match n with - | 0 | 1 => simp - | n + 2 => - intro u v h - rw [pathGraph_adj] at h - rw [cycleGraph_adj'] - cases h with - | inl h | inr h => - simp [Fin.coe_sub_iff_le.mpr (Nat.lt_of_succ_le h.le).le, Nat.eq_sub_of_add_eq' h] - -theorem cycleGraph_preconnected {n : ℕ} : (cycleGraph n).Preconnected := - (pathGraph_preconnected n).mono pathGraph_le_cycleGraph - -theorem cycleGraph_connected {n : ℕ} : (cycleGraph (n + 1)).Connected := - (pathGraph_connected n).mono pathGraph_le_cycleGraph - -section cycle - -set_option backward.privateInPublic true in -private def cycleGraph.cycleCons (n : ℕ) : ∀ m : Fin (n + 3), (cycleGraph (n + 3)).Walk m 0 - | ⟨0, h⟩ => Walk.nil - | ⟨m + 1, h⟩ => - have hadj : (cycleGraph (n + 3)).Adj ⟨m + 1, h⟩ ⟨m, Nat.lt_of_succ_lt h⟩ := by - simp [cycleGraph_adj, Fin.ext_iff, Fin.sub_val_of_le] - Walk.cons hadj (cycleGraph.cycleCons n ⟨m, Nat.lt_of_succ_lt h⟩) - -set_option backward.privateInPublic true in -set_option backward.privateInPublic.warn false in -/-- The Eulerian cycle of `cycleGraph (n + 3)` -/ -def cycleGraph.cycle (n : ℕ) : (cycleGraph (n + 3)).Walk 0 0 := - have hadj : (cycleGraph (n + 3)).Adj 0 (Fin.last (n + 2)) := by - simp [cycleGraph_adj] - Walk.cons hadj (cycleGraph.cycleCons n (Fin.last (n + 2))) - -@[deprecated (since := "2026-02-15")] -alias cycleGraph_EulerianCircuit := cycleGraph.cycle - -private theorem cycleGraph.length_cycle_cons (n : ℕ) : - ∀ m : Fin (n + 3), (cycleGraph.cycleCons n m).length = m.val - | ⟨0, h⟩ => by - unfold cycleGraph.cycleCons - rfl - | ⟨m + 1, h⟩ => by - unfold cycleGraph.cycleCons - simp only [Walk.length_cons] - rw [cycleGraph.length_cycle_cons n] - -variable {n : ℕ} - -@[simp, grind =] -theorem cycleGraph.length_cycle : (cycleGraph.cycle n).length = n + 3 := by - unfold cycleGraph.cycle - simp [cycleGraph.length_cycle_cons] - -@[deprecated (since := "2026-02-15")] -alias cycleGraph_EulerianCircuit_length := cycleGraph.length_cycle - -private theorem cycleGraph.getVert_cycleCons (m : Fin (n + 3)) (i : ℕ) (hi : i ≤ m.val) : - (cycleGraph.cycleCons n m).getVert i = (m - i) % (n + 3) := by - obtain ⟨m, hm⟩ := m - induction i generalizing m - · simp [Nat.mod_eq_of_lt hm] - · cases m <;> grind +locals [getVert_cons_succ] - -theorem cycleGraph.getVert_cycle {m : ℕ} (hm : m ≤ n + 3) : - (cycleGraph.cycle n).getVert m = ⟨(n + 3 - m) % (n + 3), Nat.mod_lt _ (by lia)⟩ := by - cases m - · simp - · grind +locals [getVert_cons_succ, cycleGraph.getVert_cycleCons] - -theorem cycleGraph.isPath_tail_cycle : (cycleGraph.cycle n).tail.IsPath := by - refine isPath_iff_injective_get_support _ |>.mpr fun ⟨i, hi⟩ ⟨j, hj⟩ hij ↦ ?_ - rw [support_tail_of_not_nil _ (of_decide_eq_false rfl)] at hi hj - simp only [List.get_eq_getElem, support_getElem_eq_getVert, getVert_tail] at hij - grind [← Nat.mod_eq_of_lt, cycleGraph.getVert_cycle] - -theorem cycleGraph.isCycle_cycle : (cycleGraph.cycle n).IsCycle := - isCycle_iff_isPath_tail_and_le_length.mpr ⟨cycleGraph.isPath_tail_cycle, by simp⟩ - -end cycle - end SimpleGraph diff --git a/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean b/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean index 0d06d645712f83..f8091d4860295c 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Coloring/Vertex.lean @@ -127,6 +127,11 @@ theorem Coloring.isIndepSet_colorClass (c : α) : G.IsIndepSet <| C.colorClass c theorem Coloring.color_classes_independent (c : α) : IsAntichain G.Adj (C.colorClass c) := C.isIndepSet_colorClass c +/-- Coloring induced from a homomorphism to a colored graph. -/ +abbrev Coloring.comap {V' : Type*} {G' : SimpleGraph V'} {α : Type*} (C : G'.Coloring α) + (f : G →g G') : G.Coloring α := + C.comp f + -- TODO make this computable noncomputable instance [Fintype V] [Fintype α] : Fintype (Coloring G α) := by classical @@ -289,9 +294,9 @@ noncomputable def Colorable.toColoring [Fintype α] {n : ℕ} (hc : G.Colorable rw [← Fintype.card_fin n] at hn exact G.recolorOfCardLE hn hc.some -theorem Colorable.of_hom {V' : Type*} {G' : SimpleGraph V'} (f : G →g G') {n : ℕ} +theorem Colorable.of_hom {V' : Type*} {G' : SimpleGraph V'} {n : ℕ} (f : G →g G') (h : G'.Colorable n) : G.Colorable n := - ⟨(h.toColoring (by simp)).comp f⟩ + ⟨h.some.comap f⟩ theorem colorable_iff_exists_bdd_nat_coloring (n : ℕ) : G.Colorable n ↔ ∃ C : G.Coloring ℕ, ∀ v, C v < n := by @@ -406,9 +411,9 @@ theorem chromaticNumber_mono (G' : SimpleGraph V) (h : G ≤ G') : G.chromaticNumber ≤ G'.chromaticNumber := chromaticNumber_le_of_forall_imp fun _ => Colorable.mono_left h -theorem chromaticNumber_mono_of_hom {V' : Type*} {G' : SimpleGraph V'} - (f : G →g G') : G.chromaticNumber ≤ G'.chromaticNumber := - chromaticNumber_le_of_forall_imp fun _ => Colorable.of_hom f +theorem chromaticNumber_mono_of_hom {V' : Type*} {G' : SimpleGraph V'} (f : G →g G') : + G.chromaticNumber ≤ G'.chromaticNumber := + chromaticNumber_le_of_forall_imp fun _ hc => hc.of_hom f lemma card_le_chromaticNumber_iff_forall_surjective [Fintype α] : card α ≤ G.chromaticNumber ↔ ∀ C : G.Coloring α, Surjective C := by @@ -615,10 +620,23 @@ theorem colorable_of_cliqueFree (f : ∀ (i : ι), V i) end completeMultipartiteGraph +variable {W : Type*} {H : SimpleGraph W} + /-- If `H` is not `n`-colorable and `G` is `n`-colorable, then `G` is `H.Free`. -/ -theorem free_of_colorable {W : Type*} {H : SimpleGraph W} - (nhc : ¬H.Colorable n) (hc : G.Colorable n) : H.Free G := by - contrapose nhc with hc' - exact ⟨hc.some.comp hc'.some.toHom⟩ +theorem free_of_colorable (nhc : ¬H.Colorable n) (hc : G.Colorable n) : H.Free G := by + contrapose! nhc with hc' + exact hc.of_hom hc'.some.toHom + +/-! ### Isomorphisms -/ + +/-- Equivalence of colorings induced by isomorphisms of graphs and equivalence of colors. -/ +def coloringCongr (f : G ≃g H) (g : α ≃ β) : G.Coloring α ≃ H.Coloring β := + f.homCongr (Iso.completeGraph g) + +lemma colorable_congr (f : G ≃g H) : G.Colorable n ↔ H.Colorable n := + ⟨fun hc ↦ hc.of_hom f.symm.toHom, fun hc ↦ hc.of_hom f.toHom⟩ + +lemma chromaticNumber_congr (f : G ≃g H) : G.chromaticNumber = H.chromaticNumber := + le_antisymm (chromaticNumber_mono_of_hom f.toHom) (chromaticNumber_mono_of_hom f.symm.toHom) end SimpleGraph diff --git a/Mathlib/Combinatorics/SimpleGraph/Connectivity/Connected.lean b/Mathlib/Combinatorics/SimpleGraph/Connectivity/Connected.lean index d0cffcec45ec13..d0cabb0db4a0e8 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Connectivity/Connected.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Connectivity/Connected.lean @@ -197,6 +197,12 @@ lemma Reachable.degree_pos_right {G : SimpleGraph V} {u v : V} [Fintype (G.neigh (huv : u ≠ v) (hreach : G.Reachable u v) : 0 < G.degree v := hreach.symm.degree_pos_left huv.symm +lemma Reachable.of_isUniversal {G : SimpleGraph V} {u : V} (v : V) (h : G.IsUniversal u) : + G.Reachable u v := by + by_cases! h' : u = v + · exact h' ▸ Reachable.rfl + · exact (h h').reachable + lemma not_reachable_of_neighborSet_left_eq_empty {G : SimpleGraph V} {u v : V} (huv : u ≠ v) (hu : G.neighborSet u = ∅) : ¬G.Reachable u v := (Reachable.nonempty_neighborSet_left huv).mt (Set.not_nonempty_iff_eq_empty.mpr hu) @@ -374,6 +380,11 @@ theorem connected_or_preconnected_compl : G.Connected ∨ Gᶜ.Preconnected := b theorem connected_or_connected_compl [Nonempty V] : G.Connected ∨ Gᶜ.Connected := G.connected_or_preconnected_compl.elim .inl (.inr ⟨·⟩) +variable {G v} in +lemma Connected.of_isUniversal (h : G.IsUniversal v) : G.Connected := by + refine connected_iff _ |>.mpr ⟨fun u w ↦ ?_, ⟨v⟩⟩ + exact (Reachable.of_isUniversal u h).symm.trans (Reachable.of_isUniversal w h) + /-- The quotient of `V` by the `SimpleGraph.Reachable` relation gives the connected components of a graph. -/ def ConnectedComponent := Quot G.Reachable diff --git a/Mathlib/Combinatorics/SimpleGraph/Connectivity/Finite.lean b/Mathlib/Combinatorics/SimpleGraph/Connectivity/Finite.lean index c1359d16dac765..0c265c4bdd44f5 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Connectivity/Finite.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Connectivity/Finite.lean @@ -114,9 +114,9 @@ lemma ncard_oddComponents_mono [Finite V] {G' : SimpleGraph V} (h : G ≤ G') : G'.oddComponents.ncard ≤ G.oddComponents.ncard := by have aux (c : G'.ConnectedComponent) (hc : Odd c.supp.ncard) : {c' : G.ConnectedComponent | Odd c'.supp.ncard ∧ c'.supp ⊆ c.supp}.Nonempty := by - refine Set.nonempty_of_ncard_ne_zero fun h' ↦ ?_ - simpa [-Nat.card_eq_fintype_card, -Set.coe_setOf, h'] - using (c.odd_oddComponents_ncard_subset_supp _ h).2 hc + refine Set.nonempty_of_ncard_ne_zero fun h' ↦ Nat.not_odd_zero ?_ + rw [← h'] + exact (c.odd_oddComponents_ncard_subset_supp _ h).2 hc let f : G'.oddComponents → G.oddComponents := fun ⟨c, hc⟩ ↦ ⟨(aux c hc).choose, (aux c hc).choose_spec.1⟩ refine Nat.card_le_card_of_injective f fun c c' fcc' ↦ ?_ diff --git a/Mathlib/Combinatorics/SimpleGraph/Copy.lean b/Mathlib/Combinatorics/SimpleGraph/Copy.lean index 3bc1aeb011955b..79ec4ae0442ef6 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Copy.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Copy.lean @@ -341,7 +341,8 @@ lemma maxDegree_mono {H : SimpleGraph V} [Fintype V] [DecidableRel G.Adj] [Decid theorem Copy.minDegree_mono [Fintype V] [Fintype W] [DecidableRel G.Adj] [DecidableRel H.Adj] {f : Copy G H} (hf : Function.Surjective f) : G.minDegree ≤ H.minDegree := by cases isEmpty_or_nonempty W - · simp [Function.isEmpty f] + · have := Function.isEmpty f + simp refine H.le_minDegree_of_forall_le_degree _ fun w ↦ ?_ obtain ⟨v, rfl⟩ := hf w grw [← f.degree_le, ← minDegree_le_degree] @@ -354,6 +355,15 @@ theorem Hom.minDegree_mono [Fintype V] [Fintype W] [DecidableRel G.Adj] [Decidab @[deprecated (since := "2026-05-20")] alias Hom.minDegree_le := Hom.minDegree_mono +theorem maxDegree_induce_of_support_subset [Fintype V] [DecidableRel G.Adj] {s : Set V} + [DecidablePred (· ∈ s)] (h : G.support ⊆ s) : (G.induce s).maxDegree = G.maxDegree := by + apply le_antisymm <| Copy.maxDegree_mono <| Embedding.induce s |>.toCopy + refine G.maxDegree_le_of_forall_degree_le _ fun v ↦ ?_ + by_cases hv : G.IsIsolated v + · simp [hv] + grw [← degree_le_maxDegree _ ⟨v, h <| G.mem_support_iff_not_isIsolated.mpr hv⟩, + degree_induce_of_neighborSet_subset <| G.neighborSet_subset_support v |>.trans h] + end IsContained section Free diff --git a/Mathlib/Combinatorics/SimpleGraph/CycleGraph.lean b/Mathlib/Combinatorics/SimpleGraph/CycleGraph.lean new file mode 100644 index 00000000000000..547fde4d0fc65f --- /dev/null +++ b/Mathlib/Combinatorics/SimpleGraph/CycleGraph.lean @@ -0,0 +1,204 @@ +/- +Copyright (c) 2024 Iván Renison, Bhavik Mehta. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Iván Renison, Bhavik Mehta +-/ +module + +public import Mathlib.Combinatorics.SimpleGraph.Hasse + +/-! +# Definition of cycle graphs + +This file defines and proves several fact about cycle graphs on `n` vertices and the cycle around +the cycle graph when `n ≥ 3`. + +## Main declarations + +* `SimpleGraph.cycleGraph n`: the cycle graph over `Fin n`. +* `(SimpleGraph.cycleGraph n).cycle`: the cycle around `cycleGraph (n + 3)` starting at 0. +-/ + +@[expose] public section + +namespace SimpleGraph + +open Walk + +/-- Cycle graph over `Fin n` -/ +def cycleGraph : (n : ℕ) → SimpleGraph (Fin n) + | 0 | 1 => ⊥ + | _ + 2 => { + Adj a b := a - b = 1 ∨ b - a = 1 + } + +instance : (n : ℕ) → DecidableRel (cycleGraph n).Adj + | 0 | 1 => fun _ _ => inferInstanceAs (Decidable False) + | _ + 2 => by unfold cycleGraph; infer_instance + +theorem cycleGraph_zero_adj {u v : Fin 0} : ¬(cycleGraph 0).Adj u v := id + +theorem cycleGraph_zero_eq_bot : cycleGraph 0 = ⊥ := Subsingleton.elim _ _ +theorem cycleGraph_one_eq_bot : cycleGraph 1 = ⊥ := Subsingleton.elim _ _ +theorem cycleGraph_zero_eq_top : cycleGraph 0 = ⊤ := Subsingleton.elim _ _ +theorem cycleGraph_one_eq_top : cycleGraph 1 = ⊤ := Subsingleton.elim _ _ + +theorem cycleGraph_two_eq_top : cycleGraph 2 = ⊤ := by + simp only [SimpleGraph.ext_iff, funext_iff] + decide + +theorem cycleGraph_three_eq_top : cycleGraph 3 = ⊤ := by + simp only [SimpleGraph.ext_iff, funext_iff] + decide + +theorem cycleGraph_one_adj {u v : Fin 1} : ¬(cycleGraph 1).Adj u v := by + simp [cycleGraph_one_eq_bot] + +theorem cycleGraph_adj {n : ℕ} {u v : Fin (n + 2)} : + (cycleGraph (n + 2)).Adj u v ↔ u - v = 1 ∨ v - u = 1 := Iff.rfl + +theorem cycleGraph_adj' {n : ℕ} {u v : Fin n} : + (cycleGraph n).Adj u v ↔ (u - v).val = 1 ∨ (v - u).val = 1 := by + match n with + | 0 => exact u.elim0 + | 1 => simp [cycleGraph_one_adj] + | n + 2 => simp [cycleGraph_adj, Fin.ext_iff] + +theorem cycleGraph_neighborSet {n : ℕ} {v : Fin (n + 2)} : + (cycleGraph (n + 2)).neighborSet v = {v - 1, v + 1} := by + ext w + simp only [mem_neighborSet, Set.mem_insert_iff, Set.mem_singleton_iff] + rw [cycleGraph_adj, sub_eq_iff_eq_add', sub_eq_iff_eq_add', eq_sub_iff_add_eq, eq_comm] + +theorem cycleGraph_neighborFinset {n : ℕ} {v : Fin (n + 2)} : + (cycleGraph (n + 2)).neighborFinset v = {v - 1, v + 1} := by + simp [neighborFinset, cycleGraph_neighborSet] + +theorem cycleGraph_degree_two_le {n : ℕ} {v : Fin (n + 2)} : + (cycleGraph (n + 2)).degree v = Finset.card {v - 1, v + 1} := by + rw [SimpleGraph.degree, cycleGraph_neighborFinset] + +theorem cycleGraph_degree_three_le {n : ℕ} {v : Fin (n + 3)} : + (cycleGraph (n + 3)).degree v = 2 := by + rw [cycleGraph_degree_two_le, Finset.card_pair] + simp only [ne_eq, sub_eq_iff_eq_add, add_assoc v, left_eq_add] + exact ne_of_beq_false rfl + +theorem pathGraph_le_cycleGraph {n : ℕ} : pathGraph n ≤ cycleGraph n := by + match n with + | 0 | 1 => simp + | n + 2 => + intro u v h + rw [pathGraph_adj] at h + rw [cycleGraph_adj'] + cases h with + | inl h | inr h => + simp [Fin.coe_sub_iff_le.mpr (Nat.lt_of_succ_le h.le).le, Nat.eq_sub_of_add_eq' h] + +theorem cycleGraph_preconnected {n : ℕ} : (cycleGraph n).Preconnected := + (pathGraph_preconnected n).mono pathGraph_le_cycleGraph + +theorem cycleGraph_connected {n : ℕ} : (cycleGraph (n + 1)).Connected := + (pathGraph_connected n).mono pathGraph_le_cycleGraph + +section cycle + +set_option backward.privateInPublic true in +private def cycleGraph.cycleCons (n : ℕ) : ∀ m : Fin (n + 3), (cycleGraph (n + 3)).Walk m 0 + | ⟨0, h⟩ => Walk.nil + | ⟨m + 1, h⟩ => + have hadj : (cycleGraph (n + 3)).Adj ⟨m + 1, h⟩ ⟨m, Nat.lt_of_succ_lt h⟩ := by + simp [cycleGraph_adj, Fin.ext_iff, Fin.sub_val_of_le] + Walk.cons hadj (cycleGraph.cycleCons n ⟨m, Nat.lt_of_succ_lt h⟩) + +set_option backward.privateInPublic true in +set_option backward.privateInPublic.warn false in +/-- The Eulerian cycle of `cycleGraph (n + 3)` -/ +def cycleGraph.cycle (n : ℕ) : (cycleGraph (n + 3)).Walk 0 0 := + have hadj : (cycleGraph (n + 3)).Adj 0 (Fin.last (n + 2)) := by + simp [cycleGraph_adj] + Walk.cons hadj (cycleGraph.cycleCons n (Fin.last (n + 2))) + +@[deprecated (since := "2026-02-15")] +alias cycleGraph_EulerianCircuit := cycleGraph.cycle + +private theorem cycleGraph.length_cycle_cons (n : ℕ) : + ∀ m : Fin (n + 3), (cycleGraph.cycleCons n m).length = m.val + | ⟨0, h⟩ => by + unfold cycleGraph.cycleCons + rfl + | ⟨m + 1, h⟩ => by + unfold cycleGraph.cycleCons + simp only [Walk.length_cons] + rw [cycleGraph.length_cycle_cons n] + +variable {n : ℕ} + +@[simp, grind =] +theorem cycleGraph.length_cycle : (cycleGraph.cycle n).length = n + 3 := by + unfold cycleGraph.cycle + simp [cycleGraph.length_cycle_cons] + +@[deprecated (since := "2026-02-15")] +alias cycleGraph_EulerianCircuit_length := cycleGraph.length_cycle + +private theorem cycleGraph.getVert_cycleCons (m : Fin (n + 3)) (i : ℕ) (hi : i ≤ m.val) : + (cycleGraph.cycleCons n m).getVert i = (m - i) % (n + 3) := by + obtain ⟨m, hm⟩ := m + induction i generalizing m + · simp [Nat.mod_eq_of_lt hm] + · cases m <;> grind +locals [getVert_cons_succ] + +theorem cycleGraph.getVert_cycle {m : ℕ} (hm : m ≤ n + 3) : + (cycleGraph.cycle n).getVert m = ⟨(n + 3 - m) % (n + 3), Nat.mod_lt _ (by lia)⟩ := by + cases m + · simp + · grind +locals [getVert_cons_succ, cycleGraph.getVert_cycleCons] + +theorem cycleGraph.isPath_tail_cycle : (cycleGraph.cycle n).tail.IsPath := by + refine isPath_iff_injective_get_support _ |>.mpr fun ⟨i, hi⟩ ⟨j, hj⟩ hij ↦ ?_ + rw [support_tail_of_not_nil _ (of_decide_eq_false rfl)] at hi hj + simp only [List.get_eq_getElem, support_getElem_eq_getVert, getVert_tail] at hij + grind [← Nat.mod_eq_of_lt, cycleGraph.getVert_cycle] + +theorem cycleGraph.isCycle_cycle : (cycleGraph.cycle n).IsCycle := + isCycle_iff_isPath_tail_and_le_length.mpr ⟨cycleGraph.isPath_tail_cycle, by simp⟩ + +end cycle + +section IsContained + +variable {V : Type*} {G : SimpleGraph V} + +lemma cycleGraph_isContained_iff {n : ℕ} (hn : 2 < n) : + cycleGraph n ⊑ G ↔ ∃ (v : V) (p : G.Walk v v), p.IsCycle ∧ p.length = n := by + refine ⟨fun ⟨h⟩ ↦ ?_, fun h' ↦ ?_⟩ + · have : n = n - 3 + 3 := by lia + rw [this] at h + refine ⟨h.toHom ⟨0, by lia⟩, Walk.map h.toHom <| cycleGraph.cycle (n - 3), ?_, ?_⟩ + · exact (isCycle_map_iff_of_injective h.injective).mpr cycleGraph.isCycle_cycle + · simp [cycleGraph.length_cycle, ← this] + · obtain ⟨a, p, hp₁, hp₂⟩ := h' + refine ⟨⟨⟨fun n ↦ p.support[n.succ]'(?_), ?_⟩, ?_⟩⟩ + · grind [hp₁.three_le_length, length_tail_add_one, not_nil_iff_lt_length] + · intro ⟨x, hx⟩ ⟨y, hy⟩ hab + have hne : x ≠ y := fun _ ↦ by simp_all + wlog hle : x > y + · exact this hn a p hp₁ hp₂ y hy x hx hab.symm hne.symm (by lia) |>.symm + rcases cycleGraph_adj'.mp hab with hab | hab + · simp_rw [show x = y + 1 by grind [Fin.sub_val_of_le]] + exact p.isChain_adj_support.getElem _ _ |>.symm + · rw [Fin.coe_sub_iff_lt.mpr hle] at hab + simp_rw [show x = n - 1 by lia, show y = 0 by lia, Fin.succ_mk, show n - 1 + 1 = n by lia] + simp [← hp₂, p.adj_snd hp₁.not_nil] + · have hlen : p.tail.support.length = n := by + grind [length_tail_add_one, not_nil_iff_lt_length] + have (m : Fin n) : p.support[m.succ]'(by grind) = p.tail.support[m] := by + simp [p.support_tail_of_not_nil hp₁.not_nil] + simp_rw [this] + have := IsPath.mk' <| (support_tail_of_not_nil _ hp₁.not_nil) ▸ hp₁.support_nodup + exact hlen ▸ (isPath_iff_injective_get_support _ |>.mp this) + +end IsContained + +end SimpleGraph diff --git a/Mathlib/Combinatorics/SimpleGraph/Extremal/ErdosStoneSimonovits.lean b/Mathlib/Combinatorics/SimpleGraph/Extremal/ErdosStoneSimonovits.lean new file mode 100644 index 00000000000000..ef28446b9a0883 --- /dev/null +++ b/Mathlib/Combinatorics/SimpleGraph/Extremal/ErdosStoneSimonovits.lean @@ -0,0 +1,328 @@ +/- +Copyright (c) 2026 Mitchell Horner. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Mitchell Horner +-/ +module + +public import Mathlib.Combinatorics.Pigeonhole +public import Mathlib.Combinatorics.SimpleGraph.Bipartite +public import Mathlib.Combinatorics.SimpleGraph.CompleteMultipartite +public import Mathlib.Analysis.Real.Sqrt + +/-! +# The Erdős-Stone-Simonovits theorem + +This file proves the **Erdős-Stone-Simonovits theorem** for simple graphs. + +## Main definitions + +* `SimpleGraph.eventually_completeEquipartiteGraph_isContained_of_minDegree` is the proof of the + minimal degree version of the **Erdős-Stone theorem** for simple graphs. +-/ + +open Filter Finset Fintype Real Topology + +namespace SimpleGraph + +variable {n : ℕ} {G : SimpleGraph (Fin n)} [DecidableRel G.Adj] + {W : Type*} [Fintype W] {H : SimpleGraph W} + +section ErdosStone + +namespace ErdosStone + +variable {ε : ℝ} {r t t' : ℕ} (K : G.CompleteEquipartiteSubgraph r t') + +/-- `ErdosStone.filter` is the set of vertices in the complement of a complete equipartite +subgraph, in `r` parts each of size `t'`, adjacent to at least `t` vertices in each part of the +complete equipartite subgraph. + +This is an auxiliary definition for the Erdős-Stone theorem. -/ +def filter (t : ℕ) : Finset (Fin n) := + { v ∈ K.vertsᶜ | ∀ p ∈ K.parts, ∃ s ∈ p.powersetCard t, ∀ w ∈ s, G.Adj v w } + +theorem filter_subset_compl_verts : filter K t ⊆ K.vertsᶜ := + filter_subset _ K.vertsᶜ + +omit [DecidableRel G.Adj] in +theorem between_verts_isBipartiteWith : + (G.between K.verts K.vertsᶜ).IsBipartiteWith K.verts ↑K.vertsᶜ := by + rw [coe_compl K.verts] + exact between_isBipartiteWith (disjoint_compl_right) + +lemma le_card_edgeFinset_between_verts : + (#K.verts * (G.minDegree - #K.verts) : ℝ) ≤ #(G.between K.verts K.vertsᶜ).edgeFinset := by + rw [← isBipartiteWith_sum_degrees_eq_card_edges (between_verts_isBipartiteWith K), + ← nsmul_eq_mul, ← sum_const, Nat.cast_sum] + exact sum_le_sum (fun v hv ↦ sub_le_iff_le_add.mpr <| + mod_cast (G.minDegree_le_degree v).trans (degree_le_between_add hv)) + +/-- For `v ∈ K.vertsᶜ \ ErdosStone.filter`, since `v` is adjacent to fewer than `t` +vertices in at least one part of the complete equipartite subgraph, it follows that `v` is +adjacent to fewer than `#K.verts - (t' - t)` vertices in `K.verts`. + +This is an auxiliary lemma for the Erdős-Stone theorem. -/ +lemma degree_between_verts_lt_of_mem_sdiff + {v : Fin n} (hv : v ∈ K.vertsᶜ \ filter K t) (ht'_pos : 0 < t') : + (G.between K.verts K.vertsᶜ).degree v < #K.verts - t' + t := by + simp_rw [Finset.mem_sdiff, ErdosStone.filter, mem_filter, not_and_or, and_or_left, + and_not_self_iff, false_or, not_forall, not_exists, not_and_or, not_forall, exists_prop] at hv + obtain ⟨hv, p, hp, hs⟩ := hv + rw [← card_neighborFinset_eq_degree, + isBipartiteWith_neighborFinset' (between_verts_isBipartiteWith K) hv] + conv => + enter [1, 1, 2] + unfold CompleteEquipartiteSubgraph.verts + rw [filter_disjiUnion, card_disjiUnion, sum_eq_sum_sdiff_singleton_add hp] + apply add_lt_add_of_le_of_lt + · conv_rhs => + rw [K.card_verts, ← Nat.sub_one_mul, ← K.card_parts.resolve_right ht'_pos.ne', + ← card_singleton p, ← Finset.card_sdiff_of_subset (singleton_subset_iff.mpr hp), + ← smul_eq_mul, ← sum_const, + ← Finset.sum_congr rfl fun _ h ↦ K.card_mem_parts (mem_sdiff.mp h).1] + exact sum_le_sum (fun _ _ ↦ card_filter_le _ _) + · contrapose! hs + obtain ⟨s, hs⟩ := powersetCard_nonempty.mpr hs + have hs' : s ∈ p.powersetCard t := powersetCard_mono (filter_subset _ _) hs + refine ⟨s, hs', fun w hw ↦ ?_⟩ + obtain ⟨_, hadj, _⟩ := by + rw [mem_powersetCard] at hs + apply hs.1 at hw + rwa [mem_filter, between_adj] at hw + exact hadj.symm + +lemma card_edgeFinset_between_verts_le (hr_pos : 0 < r) (ht'_pos : 0 < t') : + (#(G.between K.verts K.vertsᶜ).edgeFinset : ℝ) + ≤ (n - #K.verts) * (#K.verts - (t' - t)) + + #(filter K t) * (t' - t) := + calc (#(G.between K.verts K.vertsᶜ).edgeFinset : ℝ) + _ = ∑ v ∈ K.vertsᶜ \ filter K t, ((G.between K.verts K.vertsᶜ).degree v : ℝ) + + ∑ v ∈ filter K t, ((G.between K.verts K.vertsᶜ).degree v : ℝ) := by + rw [ErdosStone.filter, sum_sdiff (filter_subset _ K.vertsᶜ), eq_comm] + exact_mod_cast isBipartiteWith_sum_degrees_eq_card_edges' + (between_verts_isBipartiteWith K) + _ ≤ ∑ _ ∈ K.vertsᶜ \ filter K t, (#K.verts - t' + t : ℝ) + + ∑ _ ∈ filter K t, (#K.verts : ℝ) := by + apply add_le_add <;> refine sum_le_sum (fun v hv ↦ ?_) + · rw [← Nat.cast_sub ((Nat.le_mul_of_pos_left t' hr_pos).trans_eq K.card_verts.symm)] + exact_mod_cast (degree_between_verts_lt_of_mem_sdiff K hv ht'_pos).le + · exact_mod_cast isBipartiteWith_degree_le' + (between_verts_isBipartiteWith K) (filter_subset_compl_verts K hv) + _ = (n - #K.verts) * (#K.verts - (t' - t)) + + #(filter K t) * (t' - t) := by + rw [sum_const, nsmul_eq_mul, card_sdiff_of_subset (filter_subset_compl_verts K), + Nat.cast_sub (card_le_card (filter_subset_compl_verts K)), card_compl, + Nat.cast_sub (card_le_univ K.verts), Fintype.card_fin, sum_const, nsmul_eq_mul, sub_mul, + sub_add (#K.verts : ℝ) _ _, mul_sub (#(filter K t) : ℝ) _ _, + ← sub_add, sub_add_eq_add_sub, sub_add_cancel] + +/-- `#ErdosStone.filter` is arbitrarily large with respect to `n`. + +This is an auxiliary lemma for the Erdős-Stone theorem. -/ +lemma mul_le_card_filter_mul (hr_pos : 0 < r) (ht'_pos : 0 < t') + (hδ : G.minDegree ≥ (1 - 1 / r + ε) * n) + {N : ℕ} (hN : (N + r * t') * (t' - t) ≤ n * (r * t' * ε - t)) : + (N * (t' - t) : ℝ) ≤ (#(filter K t) * (t' - t) : ℝ) := + calc (N * (t' - t) : ℝ) + _ ≤ n * (r * t' * ε - t) - r * t' * (t' - t) := by + rw [← add_sub_cancel_right (N : ℝ) (r * t' : ℝ), sub_mul] + exact sub_le_sub_right hN _ + _ = #K.verts * ((1 - 1 / r + ε) * n - #K.verts) + - (n - #K.verts) * (#K.verts - (t' - t)) := by + conv_rhs => rw [sub_eq_add_neg, ← neg_mul, neg_sub, sub_mul, mul_sub, ← add_sub_assoc, + mul_sub, ← add_sub_assoc, sub_add_cancel, sub_right_comm, ← mul_assoc, ← mul_rotate, + mul_assoc, ← mul_sub, mul_add, mul_sub (#K.verts : ℝ) _ _, mul_one, + sub_add_eq_add_sub, add_sub_assoc, add_sub_sub_cancel, K.card_verts, Nat.cast_mul, + mul_one_div, mul_div_cancel_left₀ (t' : ℝ) (mod_cast hr_pos.ne'), sub_add_sub_cancel] + _ ≤ #K.verts * (G.minDegree - #K.verts) - (n - #K.verts) * (#K.verts - (t' - t)) := + sub_le_sub_right (mul_le_mul_of_nonneg_left + (sub_le_sub_right hδ _) (#K.verts).cast_nonneg) _ + _ ≤ #(filter K t) * (t' - t) := + sub_left_le_of_le_add <| (le_card_edgeFinset_between_verts K).trans + (card_edgeFinset_between_verts_le K hr_pos ht'_pos) + +/-- For `w ∈ ErdosStone.filter`, there exists a `r` subets of vertices of size `t < t'` +adjacent to `w`. + +This is an auxiliary definition for the Erdős-Stone theorem. -/ +noncomputable def filter.pi : + filter K t → K.parts.pi (·.powersetCard t) := + fun ⟨_, h⟩ ↦ + let s := Multiset.of_mem_filter h + ⟨fun p hp ↦ (s p hp).choose, Finset.mem_pi.mpr fun p hp ↦ (s p hp).choose_spec.1⟩ + +theorem filter.pi.mem_val {p} (hp : p ∈ K.parts) (w : filter K t) : + ∀ v ∈ (filter.pi K w).val p hp, G.Adj w v := + let s := Multiset.of_mem_filter w.prop p hp + s.choose_spec.right + +/-- If `#ErdosStone.filter` is sufficiently large, then there exist a `y` such that there +are least `t` vertices in the fiber `ErdosStone.filter.pi A · = y`. + +This is an auxiliary theorem for the Erdős-Stone theorem. -/ +theorem filter.pi.exists_le_card_fiber (hr_pos : 0 < r) (ht'_pos : 0 < t') + (ht_lt_t' : t < t') (hδ : G.minDegree ≥ (1 - 1 / r + ε) * n) + (hN : (t'.choose t ^ r * t + r * t') * (t' - t) ≤ n * (r * t' * ε - t)) : + ∃ y : K.parts.pi (·.powersetCard t), t ≤ #{ w | filter.pi K w = y } := by + have : Nonempty (K.parts.pi (·.powersetCard t)) := by + simp_rw [nonempty_coe_sort, pi_nonempty, powersetCard_nonempty] + intro p hp + rw [K.card_mem_parts hp] + exact ht_lt_t'.le + apply exists_le_card_fiber_of_mul_le_card + simp_rw [card_coe] + calc #(K.parts.pi (·.powersetCard t)) * t + _ = (∏ x ∈ K.parts, (#x).choose t) * t := by + simp_rw [Finset.card_pi, card_powersetCard] + _ = (∏ p ∈ K.parts, t'.choose t) * t := + congrArg (· * t) <| prod_congr rfl + fun p hp ↦ congrArg (Nat.choose · t) <| K.card_mem_parts hp + _ ≤ t'.choose t ^ r * t := by + rw [prod_const, K.card_parts.resolve_right ht'_pos.ne'] + _ ≤ #(filter K t) := by + refine Nat.le_of_mul_le_mul_right ?_ (Nat.sub_pos_of_lt ht_lt_t') + rw [← @Nat.cast_le ℝ, Nat.cast_mul _ (t' - t), Nat.cast_mul _ (t' - t), + Nat.cast_sub ht_lt_t'.le] + exact mul_le_card_filter_mul K hr_pos ht'_pos hδ (mod_cast hN) + +end ErdosStone + +/-- If `G` has a minimal degree of at least `(1 - 1 / r + o(1)) * n`, then `G` contains a +copy of a `completeEquipartiteGraph` in `r + 1` parts each of size `t`. + +This is the minimal-degree version of the **Erdős-Stone theorem**. -/ +public theorem eventually_completeEquipartiteGraph_isContained_of_minDegree + {ε : ℝ} (hε : 0 < ε) (r t : ℕ) : + ∀ᶠ n in atTop, ∀ {G : SimpleGraph (Fin n)} [DecidableRel G.Adj], + G.minDegree ≥ (1 - 1 / r + ε) * n + → completeEquipartiteGraph (r + 1) t ⊑ G := by + rcases show (r = 0 ∨ t = 0) ∨ r ≠ 0 ∧ t ≠ 0 by tauto with h0 | ⟨hr_pos, ht_pos⟩ + · rw [← Nat.le_zero_eq, ← @Nat.add_le_add_iff_right r 0 1, zero_add] at h0 + rw [eventually_atTop] + refine ⟨(r + 1) * t, fun n hn {G} _ _ ↦ ?_⟩ + rw [completeEquipartiteGraph_eq_bot_iff.mpr h0, bot_isContained_iff_card_le, + card_prod, Fintype.card_fin, Fintype.card_fin, Fintype.card_fin] + exact hn + · rw [← Nat.pos_iff_ne_zero] at hr_pos ht_pos + -- choose `ε'` to ensure `G.minDegree` is large enough + let ε' := 1 / (↑(r - 1) * r) + ε + have hε' : 0 < ε' := by positivity + -- choose `t'` larger than `t / (r * ε)` + let t' := ⌊t / (r * ε)⌋₊ + 1 + have ht_lt_rt'ε : t < r * t' * ε := by + rw [mul_comm (r : ℝ) (t' : ℝ), mul_assoc, ← div_lt_iff₀ (by positivity), Nat.cast_add_one] + exact Nat.lt_floor_add_one (t / (r * ε)) + have ht'_pos : 0 < t' := by positivity + have ⟨N', ih⟩ := eventually_atTop.mp <| + eventually_completeEquipartiteGraph_isContained_of_minDegree hε' (r - 1) t' + -- choose `N` at least `(t'.choose t ^ r * t + r * t') * (t '- t) / (r * t' * ε - t)` to + -- satisfy the pigeonhole principle + let N := max (max 1 N') ⌈(t'.choose t ^ r * t + r * t') * (t' - t) / (r * t' * ε - t)⌉₊ + refine eventually_atTop.mpr ⟨N, fun n hn {G} _ hδ ↦ ?_⟩ + have : Nonempty (Fin n) := by + rw [← Fin.pos_iff_nonempty] + exact hn.trans_lt' (lt_max_of_lt_left (lt_max_of_lt_left zero_lt_one)) + -- `r` is less than `1 / ε` otherwise `G.minDegree = n` + have hrε_lt_1 : r * ε < 1 := by + have hδ_lt_card : (G.minDegree : ℝ) < (n : ℝ) := by + conv_rhs => + rw [← Fintype.card_fin n] + exact_mod_cast G.minDegree_lt_card + contrapose! hδ_lt_card with h1_le_rε + rw [← div_le_iff₀' (by positivity), ← sub_nonpos, + ← le_sub_self_iff 1, ← sub_add] at h1_le_rε + exact hδ.trans' (le_mul_of_one_le_left n.cast_nonneg h1_le_rε) + have ht_lt_t' : t < t' := by + rw [mul_comm (r : ℝ) (t' : ℝ), mul_assoc] at ht_lt_rt'ε + exact_mod_cast ht_lt_rt'ε.trans_le (mul_le_of_le_one_right (mod_cast ht'_pos.le) hrε_lt_1.le) + -- identify a `completeEquipartiteGraph r t'` in `G` from the inductive hypothesis + replace ih : completeEquipartiteGraph r t' ⊑ G := by + rcases eq_or_ne r 1 with hr_eq_1 | hr_ne_1 + -- if `r = 1` then `completeEquipartiteGraph r t' = ⊥` + · have h0 : r ≤ 1 ∨ t' = 0 := Or.inl hr_eq_1.le + rw [completeEquipartiteGraph_eq_bot_iff.mpr h0, bot_isContained_iff_card_le, + card_prod, Fintype.card_fin, Fintype.card_fin, hr_eq_1, one_mul, Fintype.card_fin] + apply hn.trans' + exact_mod_cast calc (t' : ℝ) + _ ≤ r * t' := le_mul_of_one_le_left (by positivity) (mod_cast hr_pos) + _ ≤ t'.choose t ^ r * t + r * t' := le_add_of_nonneg_left (by positivity) + _ ≤ (t'.choose t ^ r * t + r * t') * (t' - t) / (r * t' * ε - t) := by + rw [mul_div_assoc, le_mul_iff_one_le_right (by positivity), + one_le_div (sub_pos.mpr ht_lt_rt'ε), sub_le_sub_iff_right, + mul_comm (r : ℝ) (t' : ℝ), mul_assoc, mul_le_iff_le_one_right (by positivity)] + exact hrε_lt_1.le + _ ≤ ⌈(t'.choose t ^ r * t + r * t') * (t' - t) / (r * t' * ε - t)⌉₊ := Nat.le_ceil _ + _ ≤ N := Nat.cast_le.mpr (le_max_right _ _) + -- if `r > 1` then `G` satisfies the inductive hypothesis + · have hδ' := calc (G.minDegree : ℝ) + _ ≥ (1 - 1 / (r - 1) + (1 / (r - 1) - 1 / r) + ε) * n := by + rwa [← sub_add_sub_cancel _ (1 / (r - 1) : ℝ) _] at hδ + _ = (1 - 1 / ↑(r - 1) + ε') * n := by + rw [← one_div_mul_sub_mul_one_div_eq_one_div_add_one_div + (sub_ne_zero_of_ne (mod_cast hr_ne_1)) (mod_cast hr_pos.ne'), + sub_sub_cancel, mul_one, one_div_mul_one_div_rev, mul_comm (r : ℝ) _, + ← Nat.cast_pred hr_pos, add_assoc] + rw [← Nat.succ_pred_eq_of_pos hr_pos] + exact ih n (hn.trans' (le_max_of_le_left (le_max_right 1 N'))) hδ' + obtain ⟨K⟩ := completeEquipartiteGraph_isContained_iff.mp ih + -- find `t` vertices not in `K` adjacent to `t` vertices in each `K.parts` using the + -- pigeonhole principle + obtain ⟨⟨y, hy⟩, ht_le_card_filter⟩ := by classical + apply ErdosStone.filter.pi.exists_le_card_fiber K hr_pos ht'_pos ht_lt_t' hδ + rw [← div_le_iff₀ (sub_pos_of_lt ht_lt_rt'ε)] + trans (N : ℝ) + · exact (Nat.le_ceil _).trans (Nat.cast_le.mpr <| le_max_right _ _) + · exact_mod_cast hn + rw [Finset.mem_pi] at hy + have ⟨s, hs_subset, hcards⟩ := exists_subset_card_eq ht_le_card_filter + -- identify the `t` vertices in each `K.parts` as a `CompleteEquipartiteSubgraph r t` in `K` + let K' : G.CompleteEquipartiteSubgraph r t := by + refine ⟨univ.map ⟨fun p : K.parts ↦ y p.val p.prop, fun p₁ p₂ (heq : y p₁ _ = y p₂ _) ↦ ?_⟩, + ?_, fun {p} hp ↦ ?_, fun p₁ hp₁ p₂ hp₂ hne v₁ hv₁ v₂ hv₂ ↦ ?_⟩ + · have hy₁' := mem_powersetCard.mp (hy p₁.val p₁.prop) + have hy₂' := mem_powersetCard.mp (hy p₂.val p₂.prop) + rw [← heq] at hy₂' + obtain ⟨v, hv⟩ : (y p₁ _).Nonempty := by + rw [← Finset.card_pos, hy₁'.right] + exact ht_pos + by_contra! hne + absurd K.isCompleteBetween p₁.prop p₂.prop + (Subtype.ext_iff.ne.mp hne) (hy₁'.left hv) (hy₂'.left hv) + exact G.loopless.irrefl v + · simp_rw [card_map, card_univ, card_coe] + exact .inl (K.card_parts.resolve_right ht'_pos.ne') + · simp_rw [univ_eq_attach, Finset.mem_map, mem_attach, + Function.Embedding.coeFn_mk, true_and, Subtype.exists] at hp + replace ⟨p, hp, hyp⟩ := hp + rw [← hyp] + have hy' := mem_powersetCard.mp (hy p hp) + exact hy'.right + · simp_rw [univ_eq_attach, coe_map, Function.Embedding.coeFn_mk, + Set.mem_image, mem_coe, mem_attach, true_and, Subtype.exists] at hp₁ hp₂ + replace ⟨p₁, hp₁, hyp₁⟩ := hp₁ + rw [← hyp₁] at hv₁ hne + have hy₁' := mem_powersetCard.mp (hy p₁ hp₁) + replace ⟨p₂, hp₂, hyp₂⟩ := hp₂ + rw [← hyp₂] at hv₂ hne + have hy₂' := mem_powersetCard.mp (hy p₂ hp₂) + refine K.isCompleteBetween hp₁ hp₂ ?_ (hy₁'.left hv₁) (hy₂'.left hv₂) + by_contra! heq + simp [← heq] at hne + -- identify the `t` vertices not in `K` and the `CompleteEquipartiteSubgraph r t` in `K` + -- as a `CompleteEquipartiteSubgraph (r + 1) t` in `G` + refine completeEquipartiteGraph_succ_isContained_iff.mpr + ⟨K', s.map (.subtype _), by rwa [← card_map] at hcards, fun p' hp' v hv w hw ↦ ?_⟩ + obtain ⟨w', hw'_mem, (hw'_eq : ↑w' = w)⟩ := Finset.mem_map.mp hw + simp_rw [K', univ_eq_attach, Finset.mem_map, mem_attach, + Function.Embedding.coeFn_mk, true_and, Subtype.exists] at hp' + obtain ⟨p, hp, hp'_eq⟩ : ∃ p, ∃ (h : p ∈ K.parts), y p h = p' := hp' + apply hs_subset at hw'_mem + simp_rw [mem_filter, mem_univ, true_and, ErdosStone.filter.pi, Subtype.mk.injEq] at hw'_mem + rw [← hp'_eq, mem_coe, ← hw'_mem] at hv + rw [← hw'_eq] + exact (ErdosStone.filter.pi.mem_val K hp w' v hv).symm + +end ErdosStone + +end SimpleGraph diff --git a/Mathlib/Combinatorics/SimpleGraph/Finite.lean b/Mathlib/Combinatorics/SimpleGraph/Finite.lean index c5f2e356c5f83c..e8b42995533a41 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Finite.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Finite.lean @@ -385,10 +385,8 @@ theorem exists_minimal_degree_vertex [DecidableRel G.Adj] [Nonempty V] : grind [minDegree, WithTop.untopD_coe, min_mem_image_coe <| univ_nonempty.image (G.degree ·)] /-- The minimum degree in the graph is at most the degree of any particular vertex. -/ -theorem minDegree_le_degree [DecidableRel G.Adj] (v : V) : G.minDegree ≤ G.degree v := by - obtain ⟨t, ht⟩ := Finset.min_of_mem (mem_image_of_mem (fun v => G.degree v) (mem_univ v)) - have := Finset.min_le_of_eq (mem_image_of_mem _ (mem_univ v)) ht - rwa [minDegree, ht] +theorem minDegree_le_degree [DecidableRel G.Adj] (v : V) : G.minDegree ≤ G.degree v := + WithTop.untopD_le <| Finset.min_le <| mem_image_of_mem (G.degree ·) <| mem_univ v /-- In a nonempty graph, if `k` is at most the degree of every vertex, it is at most the minimum degree. Note the assumption that the graph is nonempty is necessary as long as `G.minDegree` is @@ -399,14 +397,16 @@ theorem le_minDegree_of_forall_le_degree [DecidableRel G.Adj] [Nonempty V] (k : rw [hv] apply h -/-- If there are no vertices then the `minDegree` is zero. -/ @[simp] -lemma minDegree_of_isEmpty [DecidableRel G.Adj] [IsEmpty V] : G.minDegree = 0 := by - rw [minDegree, WithTop.untopD_eq_self_iff] - simp +lemma minDegree_of_subsingleton [DecidableRel G.Adj] [Subsingleton V] : G.minDegree = 0 := by + cases isEmpty_or_nonempty V <;> + simp [minDegree, Finset.image_const] + +@[deprecated (since := "2026-06-15")] alias minDegree_of_isEmpty := minDegree_of_subsingleton variable {G} in /-- If `G` is a subgraph of `H` then `G.minDegree ≤ H.minDegree`. -/ +@[gcongr] lemma minDegree_le_minDegree {H : SimpleGraph V} [DecidableRel G.Adj] [DecidableRel H.Adj] (hle : G ≤ H) : G.minDegree ≤ H.minDegree := by cases isEmpty_or_nonempty V @@ -438,8 +438,11 @@ theorem degree_le_maxDegree [DecidableRel G.Adj] (v : V) : G.degree v ≤ G.maxD WithBot.le_unbotD <| Finset.le_max <| mem_image_of_mem (G.degree ·) <| mem_univ v @[simp] -lemma maxDegree_of_isEmpty [DecidableRel G.Adj] [IsEmpty V] : G.maxDegree = 0 := by - rw [maxDegree, univ_eq_empty, image_empty, max_empty, WithBot.unbotD_bot] +lemma maxDegree_of_subsingleton [DecidableRel G.Adj] [Subsingleton V] : G.maxDegree = 0 := by + cases isEmpty_or_nonempty V <;> + simp [maxDegree, Finset.image_const] + +@[deprecated (since := "2026-06-15")] alias maxDegree_of_isEmpty := maxDegree_of_subsingleton /-- In a graph, if `k` is at least the degree of every vertex, then it is at least the maximum degree. -/ @@ -458,6 +461,16 @@ theorem IsRegularOfDegree.maxDegree_eq [Nonempty V] [DecidableRel G.Adj] {d : lemma maxDegree_bot_eq_zero : (⊥ : SimpleGraph V).maxDegree = 0 := Nat.le_zero.1 <| maxDegree_le_of_forall_degree_le _ _ (by simp) +variable {G} in +@[simp] +theorem maxDegree_eq_zero_iff [DecidableRel G.Adj] : G.maxDegree = 0 ↔ G = ⊥ := by + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · rw [eq_bot_iff_isIsolated] + intro v + grind [degree_eq_zero, G.degree_le_maxDegree v] + · convert maxDegree_bot_eq_zero + assumption + @[simp] lemma maxDegree_top [DecidableEq V] : (⊤ : SimpleGraph V).maxDegree = Fintype.card V - 1 := by cases isEmpty_or_nonempty V @@ -478,6 +491,18 @@ theorem IsRegularOfDegree.minDegree_eq [Nonempty V] [DecidableRel G.Adj] {d : lemma minDegree_bot_eq_zero : (⊥ : SimpleGraph V).minDegree = 0 := Nat.le_zero.1 <| (minDegree_le_maxDegree _).trans (by simp) +variable {G} in +theorem minDegree_eq_zero_iff [DecidableRel G.Adj] [Nonempty V] : + G.minDegree = 0 ↔ ∃ v, G.IsIsolated v := by + refine ⟨fun h ↦ ?_, fun ⟨v, hv⟩ ↦ ?_⟩ + · grind [G.exists_minimal_degree_vertex, degree_eq_zero] + · grind [G.minDegree_le_degree v, degree_eq_zero] + +variable {G} in +theorem minDegree_eq_zero_iff_support_ne [DecidableRel G.Adj] [Nonempty V] : + G.minDegree = 0 ↔ G.support ≠ .univ := by + simp [Set.ne_univ_iff_exists_notMem, minDegree_eq_zero_iff] + @[simp] lemma minDegree_top [DecidableEq V] : (⊤ : SimpleGraph V).minDegree = Fintype.card V - 1 := by cases isEmpty_or_nonempty V @@ -522,6 +547,28 @@ theorem card_commonNeighbors_top [DecidableEq V] {v w : V} (h : v ≠ w) : Fintype.card (commonNeighbors ⊤ v w) = Fintype.card V - 2 := by simp [commonNeighbors_top_eq, ← Set.toFinset_card, Finset.card_sdiff, h] +@[simp] lemma insert_neighborFinset_eq_univ [DecidableEq V] [DecidableRel G.Adj] (v : V) : + insert v (G.neighborFinset v) = univ ↔ G.IsUniversal v := by + simp only [Finset.ext_iff, mem_insert, mem_neighborFinset, IsUniversal] + grind + +@[simp] lemma neighborFinset_eq_erase_univ [DecidableEq V] [DecidableRel G.Adj] (v : V) : + G.neighborFinset v = univ.erase v ↔ G.IsUniversal v := by + grind [insert_neighborFinset_eq_univ, notMem_neighborFinset_self] + +@[simp] +lemma degree_eq_card_sub_one [DecidableRel G.Adj] (v : V) : + G.degree v = Fintype.card V - 1 ↔ G.IsUniversal v := by + classical + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · rw [← G.insert_neighborFinset_eq_univ v, ← Finset.card_eq_iff_eq_univ] + simp [h, Nat.sub_add_cancel <| Fintype.card_pos_iff.mpr ⟨v⟩] + · simp [← card_neighborFinset_eq_degree, (G.neighborFinset_eq_erase_univ v).mpr h] + +lemma degree_lt_card_sub_one [DecidableRel G.Adj] (v : V) : + G.degree v < Fintype.card V - 1 ↔ ¬ G.IsUniversal v := by + grind [degree_eq_card_sub_one, Nat.le_sub_one_of_lt <| G.degree_lt_card_verts v] + end Finite namespace Iso @@ -613,6 +660,17 @@ theorem degree_induce_support (v : G.support) : (G.induce G.support).degree v = G.degree v := degree_induce_of_support_subset subset_rfl v +theorem le_minDegree_induce_of_support_subset (h : G.support ⊆ s) : + G.minDegree ≤ (G.induce s).minDegree := by + cases isEmpty_or_nonempty V + · simp + rcases s.eq_empty_or_nonempty with (rfl | hs) + · simp [minDegree_eq_zero_iff_support_ne, Set.subset_empty_iff.mp h, Set.empty_ne_univ] + have := hs.to_subtype + refine le_minDegree_of_forall_le_degree _ _ fun v ↦ ?_ + grw [G.minDegree_le_degree v, degree_induce_of_neighborSet_subset] + grw [neighborSet_subset_support, h] + end Support section Map diff --git a/Mathlib/Combinatorics/SimpleGraph/FiveWheelLike.lean b/Mathlib/Combinatorics/SimpleGraph/FiveWheelLike.lean index 16d779a23d9d33..ec4282dafd2578 100644 --- a/Mathlib/Combinatorics/SimpleGraph/FiveWheelLike.lean +++ b/Mathlib/Combinatorics/SimpleGraph/FiveWheelLike.lean @@ -20,7 +20,7 @@ structures as well as graphs which avoid this structure. These have two key uses first appeared. We give this proof below, see `colorable_of_cliqueFree_lt_minDegree`. If `G` is maximally `Kᵣ₊₂`-free and `¬ G.Adj x y` (with `x ≠ y`) then there exists an `r`-set `s` - such that `s ∪ {x}` and `s ∪ {y}` are both `r + 1`-cliques. +such that `s ∪ {x}` and `s ∪ {y}` are both `r + 1`-cliques. If `¬ G.IsCompleteMultipartite` then it contains a `G.IsPathGraph3Compl v w₁ w₂` consisting of an edge `w₁w₂` and a vertex `v` such that `vw₁` and `vw₂` are non-edges. @@ -28,7 +28,7 @@ an edge `w₁w₂` and a vertex `v` such that `vw₁` and `vw₂` are non-edges. Hence any maximally `Kᵣ₊₂`-free graph that is not complete-multipartite must contain distinct vertices `v, w₁, w₂`, together with `r`-sets `s` and `t`, such that `{v, w₁, w₂}` induces the single edge `w₁w₂`, `s ∪ t` is disjoint from `{v, w₁, w₂}`, and `s ∪ {v}`, `t ∪ {v}`, `s ∪ {w₁}` and - `t ∪ {w₂}` are all `r + 1`-cliques. +`t ∪ {w₂}` are all `r + 1`-cliques. This leads to the definition of an `IsFiveWheelLike` structure which can be found in any maximally `Kᵣ₊₂`-free graph that is not complete-multipartite (see diff --git a/Mathlib/Combinatorics/SimpleGraph/Girth.lean b/Mathlib/Combinatorics/SimpleGraph/Girth.lean index 13b8b3f6dfd40e..7b9df7bf40e396 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Girth.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Girth.lean @@ -16,8 +16,8 @@ cycle, they give `0` or `∞` respectively if the graph is acyclic. ## TODO -- Prove that `G.egirth ≤ 2 * G.ediam + 1` and `G.girth ≤ 2 * G.diam + 1` when the diameter is - non-zero. +- Prove that `G.egirth ≤ 2 * G.ediam + 1` when `G` is not acyclic +- Prove that `G.girth ≤ 2 * G.diam + 1` when the diameter is non-zero -/ @@ -43,6 +43,17 @@ lemma le_egirth {n : ℕ∞} : n ≤ G.egirth ↔ ∀ a (w : G.Walk a a), w.IsCy lemma egirth_le_length {a} {w : G.Walk a a} (h : w.IsCycle) : G.egirth ≤ w.length := le_egirth.mp le_rfl a w h +lemma Walk.IsCircuit.egirth_le_length {a} {w : G.Walk a a} (hwc : w.IsCircuit) : + G.egirth ≤ w.length := by + classical + by_contra! hlg + let w' : G.Walk a a := w.cycleBypass + have hwc' : w'.IsCycle := hwc.isCycle_cycleBypass + have hwlg' : w'.length < G.egirth := by + grw [w.length_cycleBypass_le_length] + exact hlg + exact not_le_of_gt hwlg' (SimpleGraph.egirth_le_length hwc') + @[simp] lemma egirth_eq_top : G.egirth = ⊤ ↔ G.IsAcyclic := by simp [egirth, IsAcyclic] @@ -62,10 +73,23 @@ lemma exists_egirth_eq_length : exact ciInf_mem _ lemma three_le_egirth : 3 ≤ G.egirth := by - simpa using fun _ _ a ↦ Walk.IsCycle.three_le_length a + simpa using fun _ _ h ↦ h.three_le_length @[simp] lemma egirth_bot : egirth (⊥ : SimpleGraph α) = ⊤ := by simp +theorem egirth_top (h : 3 ≤ ENat.card α) : egirth (⊤ : SimpleGraph α) = 3 := by + classical + refine le_antisymm ?_ three_le_egirth + obtain ⟨s, hcard⟩ := Cardinal.exists_finset_eq_card <| Cardinal.ofNat_le_toENat.mp h + obtain ⟨x, y, z, hxy, hxz, hyz, -⟩ := s.card_eq_three.mp hcard.symm + set w : Walk ⊤ x x := .cons hxy <| .cons hyz <| .cons hxz.symm .nil with hw + have : w.IsCycle := + { edges_nodup := by aesop + ne_nil := by aesop + support_nodup := by aesop } + grw [egirth_le_length this] + simp [hw] + @[gcongr only] lemma IsContained.egirth_le (h : G ⊑ G') : G'.egirth ≤ G.egirth := by by_cases hacyc : G.IsAcyclic @@ -104,6 +128,10 @@ protected alias ⟨_, IsAcyclic.girth_eq_zero⟩ := girth_eq_zero lemma girth_anti {G' : SimpleGraph α} (hab : G ≤ G') (h : ¬ G.IsAcyclic) : G'.girth ≤ G.girth := ENat.toNat_le_toNat (egirth_anti hab) <| egirth_eq_top.not.mpr h +lemma Walk.IsCircuit.girth_le_length {a} {w : G.Walk a a} (hwc : w.IsCircuit) : + G.girth ≤ w.length := + ENat.coe_le_coe.mp <| G.egirth.coe_toNat_le_self.trans <| hwc.egirth_le_length + lemma exists_girth_eq_length : (∃ (a : α) (w : G.Walk a a), w.IsCycle ∧ G.girth = w.length) ↔ ¬ G.IsAcyclic := by refine ⟨by tauto, fun h ↦ ?_⟩ @@ -114,6 +142,9 @@ lemma exists_girth_eq_length : @[simp] lemma girth_bot : girth (⊥ : SimpleGraph α) = 0 := by simp [girth] +theorem girth_top (h : 3 ≤ ENat.card α) : girth (⊤ : SimpleGraph α) = 3 := by + simp [girth, egirth_top h] + lemma IsContained.girth_le (h : G ⊑ G') (hG : ¬G.IsAcyclic) : G'.girth ≤ G.girth := ENat.toNat_le_toNat h.egirth_le <| egirth_eq_top.not.mpr hG diff --git a/Mathlib/Combinatorics/SimpleGraph/Maps.lean b/Mathlib/Combinatorics/SimpleGraph/Maps.lean index 07a8cc9ab92df0..404a8eca0f79ef 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Maps.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Maps.lean @@ -730,6 +730,9 @@ theorem toEmbedding_completeGraph {α β : Type*} (f : α ≃ β) : variable {G'' : SimpleGraph X} {G''' : SimpleGraph Y} +/-- Equivalence of homomorphisms induced by isomorphisms of graphs. -/ +abbrev homCongr (f' : G'' ≃g G''') : G →g G'' ≃ G' →g G''' := RelIso.relHomCongr f f' + /-- Composition of graph isomorphisms. -/ abbrev comp (f' : G' ≃g G'') (f : G ≃g G') : G ≃g G'' := f.trans f' diff --git a/Mathlib/Combinatorics/SimpleGraph/Paths.lean b/Mathlib/Combinatorics/SimpleGraph/Paths.lean index a5813a65160535..6f69fbe25693eb 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Paths.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Paths.lean @@ -298,25 +298,27 @@ lemma IsPath.ne_of_mem_support_of_append {p : G.Walk u v} {q : G.Walk v w} exact IsPath.disjoint_support_of_append hpq hq hx hx' @[simp] -theorem IsCycle.not_of_nil {u : V} : ¬(nil : G.Walk u u).IsCycle := fun h => h.ne_nil rfl +theorem not_isCircuit_nil {u : V} : ¬(nil : G.Walk u u).IsCircuit := + (·.ne_nil rfl) -lemma IsCycle.ne_bot : ∀ {p : G.Walk u u}, p.IsCycle → G ≠ ⊥ - | nil, hp => by cases hp.ne_nil rfl +@[simp] +theorem not_isCycle_nil {u : V} : ¬(nil : G.Walk u u).IsCycle := + (·.ne_nil rfl) + +@[deprecated (since := "2026-06-16")] alias IsCycle.not_of_nil := not_isCycle_nil + +lemma IsCircuit.ne_bot : ∀ {p : G.Walk u u}, p.IsCircuit → G ≠ ⊥ | cons h _, hp => by rintro rfl; exact h -lemma IsCycle.three_le_length {v : V} {p : G.Walk v v} (hp : p.IsCycle) : 3 ≤ p.length := by - have ⟨⟨hp, hp'⟩, _⟩ := hp +lemma IsCircuit.three_le_length {p : G.Walk v v} (hp : p.IsCircuit) : 3 ≤ p.length := by match p with - | .nil => simp at hp' - | .cons h .nil => simp at h - | .cons _ (.cons _ .nil) => simp at hp - | .cons _ (.cons _ (.cons _ _)) => simp_rw [SimpleGraph.Walk.length_cons]; lia + | .cons hadj .nil => simp at hadj + | .cons _ <| .cons _ .nil => simpa using hp.isTrail + | .cons _ <| .cons _ <| .cons _ _ => grind [length_cons] lemma not_nil_of_isCycle_cons {p : G.Walk u v} {h : G.Adj v u} (hc : (Walk.cons h p).IsCycle) : ¬ p.Nil := by - have := Walk.length_cons _ _ ▸ Walk.IsCycle.three_le_length hc - rw [Walk.not_nil_iff_lt_length] - lia + grind [not_nil_iff_lt_length, hc.three_le_length, length_cons] theorem cons_isCycle_iff {u v : V} (p : G.Walk v u) (h : G.Adj u v) : (Walk.cons h p).IsCycle ↔ p.IsPath ∧ s(u, v) ∉ p.edges := by @@ -358,6 +360,9 @@ lemma IsPath.tail {p : G.Walk u v} (hp : p.IsPath) : p.tail.IsPath := by | cons hadj p => simp_all [Walk.isPath_def] +theorem IsCycle.isPath_dropLast {p : G.Walk u u} (h : p.IsCycle) : p.dropLast.IsPath := + .mk' <| p.support_dropLast h.not_nil ▸ h.nodup_dropLast_support + theorem IsPath.dropLast (hp : p.IsPath) : p.dropLast.IsPath := hp.take _ @@ -902,6 +907,10 @@ lemma bypass_eq_self_of_length_le_length_bypass (p : G.Walk u v) (h : p.length @[deprecated (since := "2026-05-25")] alias bypass_eq_self_of_length_le := bypass_eq_self_of_length_le_length_bypass +@[grind →] +lemma IsPath.bypass_eq_self {p : G.Walk u v} (hp : p.IsPath) : p.bypass = p := by + induction p <;> simp_all [cons_isPath_iff, bypass] + theorem darts_toPath_subset_darts (p : G.Walk u v) : (p.toPath : G.Walk u v).darts ⊆ p.darts := p.darts_bypass_subset_darts @@ -965,68 +974,100 @@ end Walk namespace Walk -variable {G G'} -variable (f : G →g G') {u v : V} (p : G.Walk u v) -variable {p f} - -theorem map_isPath_of_injective (hinj : Function.Injective f) (hp : p.IsPath) : - (p.map f).IsPath := by - induction p with - | nil => simp - | cons _ _ ih => - rw [Walk.cons_isPath_iff] at hp - simp only [map_cons, cons_isPath_iff, ih hp.1, support_map, List.mem_map, not_exists, not_and, - true_and] - intro x hx hf - cases hinj hf - exact hp.2 hx - -protected theorem IsPath.of_map {f : G →g G'} (hp : (p.map f).IsPath) : p.IsPath := by - induction p with - | nil => simp - | cons _ _ ih => grind [map_cons, Walk.cons_isPath_iff, support_map] +variable {G G'} {f : G →g G'} {u v : V} {p : G.Walk u v} -theorem map_isPath_iff_of_injective (hinj : Function.Injective f) : (p.map f).IsPath ↔ p.IsPath := - ⟨IsPath.of_map, map_isPath_of_injective hinj⟩ +protected theorem IsTrail.of_map (hp : (p.map f).IsTrail) : p.IsTrail := by + rw [isTrail_def] + rw [isTrail_def, edges_map] at hp + exact hp.of_map -theorem map_isTrail_iff_of_injective (hinj : Function.Injective f) : +theorem isTrail_map_iff_of_injective (hinj : Function.Injective f) : (p.map f).IsTrail ↔ p.IsTrail := by - induction p with - | nil => simp - | cons _ _ ih => - rw [map_cons, isTrail_cons, ih, isTrail_cons] - apply and_congr_right' - rw [← Sym2.map_mk, edges_map, ← List.mem_map_of_injective (Sym2.map.injective hinj)] + rw [isTrail_def, isTrail_def, edges_map, List.nodup_map_iff <| Sym2.map.injective hinj] + +@[deprecated (since := "2026-06-16")] +alias map_isTrail_iff_of_injective := isTrail_map_iff_of_injective + +alias ⟨_, IsTrail.map⟩ := isTrail_map_iff_of_injective -alias ⟨_, map_isTrail_of_injective⟩ := map_isTrail_iff_of_injective +@[deprecated (since := "2026-06-16")] alias isTrmap_ail_of_injective := IsTrail.map -theorem map_isCycle_iff_of_injective {p : G.Walk u u} (hinj : Function.Injective f) : +protected theorem IsPath.of_map (hp : (p.map f).IsPath) : p.IsPath := by + rw [isPath_def] + rw [isPath_def, support_map] at hp + exact hp.of_map + +theorem isPath_map_iff_of_injective (hinj : Function.Injective f) : + (p.map f).IsPath ↔ p.IsPath := by + rw [isPath_def, isPath_def, support_map, List.nodup_map_iff hinj] + +@[deprecated (since := "2026-06-16")] +alias map_isPath_iff_of_injective := isPath_map_iff_of_injective + +alias ⟨_, IsPath.map⟩ := isPath_map_iff_of_injective + +@[deprecated (since := "2026-06-16")] alias isPmap_ath_of_injective := IsPath.map + +protected theorem IsCircuit.of_map {p : G.Walk u u} (hp : (p.map f).IsCircuit) : p.IsCircuit := by + rw [isCircuit_def, ne_eq, eq_nil_iff_nil] + rw [isCircuit_def, ne_eq, eq_nil_iff_nil, nil_map_iff] at hp + exact hp.imp_left .of_map + +theorem isCircuit_map_iff_of_injective {p : G.Walk u u} (hinj : Function.Injective f) : + (p.map f).IsCircuit ↔ p.IsCircuit := by + rw [isCircuit_def, isCircuit_def, isTrail_map_iff_of_injective hinj, ne_eq, ne_eq, eq_nil_iff_nil, + eq_nil_iff_nil, nil_map_iff] + +alias ⟨_, IsCircuit.map⟩ := isCircuit_map_iff_of_injective + +protected theorem IsCycle.of_map {p : G.Walk u u} (hp : (p.map f).IsCycle) : p.IsCycle := by + rw [isCycle_def, ne_eq, eq_nil_iff_nil] + rw [isCycle_def, ne_eq, eq_nil_iff_nil, nil_map_iff, support_map, ← List.map_tail] at hp + exact hp.imp .of_map <| .imp_right <| .of_map f + +theorem isCycle_map_iff_of_injective {p : G.Walk u u} (hinj : Function.Injective f) : (p.map f).IsCycle ↔ p.IsCycle := by - rw [isCycle_def, isCycle_def, map_isTrail_iff_of_injective hinj, ne_eq, ne_eq, eq_nil_iff_nil, + rw [isCycle_def, isCycle_def, isTrail_map_iff_of_injective hinj, ne_eq, ne_eq, eq_nil_iff_nil, eq_nil_iff_nil, nil_map_iff, support_map, ← List.map_tail, List.nodup_map_iff hinj] -alias ⟨_, IsCycle.map⟩ := map_isCycle_iff_of_injective +@[deprecated (since := "2026-06-16")] +alias map_isCycle_iff_of_injective := isCycle_map_iff_of_injective + +alias ⟨_, IsCycle.map⟩ := isCycle_map_iff_of_injective @[simp] -theorem mapLe_isTrail {G G' : SimpleGraph V} (h : G ≤ G') {u v : V} {p : G.Walk u v} : +theorem isTrail_mapLe {G G' : SimpleGraph V} (h : G ≤ G') {u v : V} {p : G.Walk u v} : (p.mapLe h).IsTrail ↔ p.IsTrail := - map_isTrail_iff_of_injective Function.injective_id + isTrail_map_iff_of_injective Function.injective_id -alias ⟨IsTrail.of_mapLe, IsTrail.mapLe⟩ := mapLe_isTrail +@[deprecated (since := "2026-06-16")] alias mapLe_isTrail := isTrail_mapLe + +alias ⟨IsTrail.of_mapLe, IsTrail.mapLe⟩ := isTrail_mapLe @[simp] -theorem mapLe_isPath {G G' : SimpleGraph V} (h : G ≤ G') {u v : V} {p : G.Walk u v} : +theorem isPath_mapLe {G G' : SimpleGraph V} (h : G ≤ G') {u v : V} {p : G.Walk u v} : (p.mapLe h).IsPath ↔ p.IsPath := - map_isPath_iff_of_injective Function.injective_id + isPath_map_iff_of_injective Function.injective_id + +@[deprecated (since := "2026-06-16")] alias mapLe_isPath := isPath_mapLe -alias ⟨IsPath.of_mapLe, IsPath.mapLe⟩ := mapLe_isPath +alias ⟨IsPath.of_mapLe, IsPath.mapLe⟩ := isPath_mapLe @[simp] -theorem mapLe_isCycle {G G' : SimpleGraph V} (h : G ≤ G') {u : V} {p : G.Walk u u} : +theorem isCircuit_mapLe {G G' : SimpleGraph V} (h : G ≤ G') {u : V} {p : G.Walk u u} : + (p.mapLe h).IsCircuit ↔ p.IsCircuit := + isCircuit_map_iff_of_injective Function.injective_id + +alias ⟨IsCircuit.of_mapLe, IsCircuit.mapLe⟩ := isCircuit_mapLe + +@[simp] +theorem isCycle_mapLe {G G' : SimpleGraph V} (h : G ≤ G') {u : V} {p : G.Walk u u} : (p.mapLe h).IsCycle ↔ p.IsCycle := - map_isCycle_iff_of_injective Function.injective_id + isCycle_map_iff_of_injective Function.injective_id + +@[deprecated (since := "2026-06-16")] alias mapLe_isCycle := isCycle_mapLe -alias ⟨IsCycle.of_mapLe, IsCycle.mapLe⟩ := mapLe_isCycle +alias ⟨IsCycle.of_mapLe, IsCycle.mapLe⟩ := isCycle_mapLe end Walk @@ -1038,7 +1079,7 @@ variable {G G'} @[simps] protected def map (f : G →g G') (hinj : Function.Injective f) {u v : V} (p : G.Path u v) : G'.Path (f u) (f v) := - ⟨Walk.map f p, Walk.map_isPath_of_injective hinj p.2⟩ + ⟨Walk.map f p, p.isPath.map hinj⟩ theorem map_injective {f : G →g G'} (hinj : Function.Injective f) (u v : V) : Function.Injective (Path.map f hinj : G.Path u v → G'.Path (f u) (f v)) := by diff --git a/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean b/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean index 10020841234a53..308abad39b08c1 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Regularity/Bound.lean @@ -239,10 +239,10 @@ open Lean.Meta Qq /-- Extension for the `positivity` tactic: `SzemerediRegularity.initialBound` is always positive. -/ @[positivity SzemerediRegularity.initialBound _ _] -meta def evalInitialBound : PositivityExt where eval {u α} _ pα? e := do +meta def evalInitialBound : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(SzemerediRegularity.initialBound $ε $l) => - let some _ := pα? | pure .none assertInstancesCommute pure (.positive q(SzemerediRegularity.initialBound_pos $ε $l)) | _, _, _ => throwError "not initialBound" @@ -252,10 +252,10 @@ example (ε : ℝ) (l : ℕ) : 0 < SzemerediRegularity.initialBound ε l := by p /-- Extension for the `positivity` tactic: `SzemerediRegularity.bound` is always positive. -/ @[positivity SzemerediRegularity.bound _ _] -meta def evalBound : PositivityExt where eval {u α} _ pα? e := do +meta def evalBound : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(SzemerediRegularity.bound $ε $l) => - let some _ := pα? | pure .none assertInstancesCommute pure (.positive q(SzemerediRegularity.bound_pos $ε $l)) | _, _, _ => throwError "not bound" diff --git a/Mathlib/Combinatorics/SimpleGraph/Star.lean b/Mathlib/Combinatorics/SimpleGraph/Star.lean index 13dc86ac80611e..f0a5ada3e89826 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Star.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Star.lean @@ -34,7 +34,7 @@ variable {V V' : Type*} (G : SimpleGraph V) (G' : SimpleGraph V') /-- The star graph on `V` centered at `r`: every non-center vertex is adjacent to `r`. -/ def starGraph (r : V) : SimpleGraph V := - .fromRel fun v _ ↦ v = r + .fromRel fun v _ ↦ v = r instance [DecidableEq V] (r : V) : DecidableRel (starGraph r).Adj := inferInstanceAs (DecidableRel fun x y ↦ x ≠ y ∧ (x = r ∨ y = r)) @@ -43,6 +43,11 @@ instance [DecidableEq V] (r : V) : DecidableRel (starGraph r).Adj := lemma starGraph_adj {r x y : V} : (starGraph r).Adj x y ↔ x ≠ y ∧ (x = r ∨ y = r) := by simp [starGraph, fromRel] +@[simp] +lemma isUniversal_starGraph_self {r : V} : (starGraph r).IsUniversal r := by + intro _ _ + simpa + /-- On (starGraph r), r is adjacent to v iff v ≠ r. -/ lemma starGraph_adj_center_iff {r v : V} : (starGraph r).Adj r v ↔ r ≠ v := by simp @@ -52,12 +57,8 @@ lemma starGraph_center_adj {r v : V} (h : r ≠ v) : (starGraph r).Adj r v := lemma starGraph_center_adj' {r v : V} (h : r ≠ v) : (starGraph r).Adj v r := (starGraph_center_adj h).symm -lemma connected_starGraph (r : V) : (starGraph r).Connected := by - have (v : V) : (starGraph r).Reachable r v := by - by_cases! h : r = v - · exact h ▸ Reachable.rfl - · exact (starGraph_center_adj h).reachable - exact connected_iff _ |>.mpr ⟨fun u v ↦ (this u).symm.trans (this v), ⟨r⟩⟩ +lemma connected_starGraph (r : V) : (starGraph r).Connected := + .of_isUniversal isUniversal_starGraph_self lemma isAcyclic_starGraph (r : V) : (starGraph r).IsAcyclic := by refine isAcyclic_iff_forall_adj_isBridge.mpr fun v w hadj ↦ ?_ @@ -81,6 +82,6 @@ lemma degree_starGraph_of_ne_center [Fintype V] [DecidableEq V] {r v : V} (h : v /-- The center vertex of a starGraph has degree (card V) - 1. -/ lemma degree_starGraph_center [Fintype V] [DecidableEq V] {r : V} : (starGraph r).degree r = Fintype.card V - 1 := by - simp [degree, neighborFinset_eq_filter (starGraph r), starGraph_adj, Finset.univ.filter_ne r] + simp end SimpleGraph diff --git a/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean b/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean index 2e4f7bb17a6c94..3377d40e399d70 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Triangle/Removal.lean @@ -176,10 +176,10 @@ if `ε` is. This exploits the positivity of the junk value of `triangleRemovalBound ε` for `ε ≥ 1`. -/ @[positivity triangleRemovalBound _] -meta def evalTriangleRemovalBound : PositivityExt where eval {u α} _zα pα? e := do +meta def evalTriangleRemovalBound : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(triangleRemovalBound $ε) => - let some _ := pα? | pure .none let .positive hε ← core q(inferInstance) (some q(inferInstance)) ε | failure assertInstancesCommute pure (.positive q(triangleRemovalBound_pos $hε)) diff --git a/Mathlib/Combinatorics/SimpleGraph/Tutte.lean b/Mathlib/Combinatorics/SimpleGraph/Tutte.lean index e905473f5226e5..7640e0a025927f 100644 --- a/Mathlib/Combinatorics/SimpleGraph/Tutte.lean +++ b/Mathlib/Combinatorics/SimpleGraph/Tutte.lean @@ -294,21 +294,21 @@ lemma exists_isTutteViolator (h : ∀ (M : G.Subgraph), ¬M.IsPerfectMatching) 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 [ConnectedComponent.toSimpleGraph, deleteUniversalVerts, universalVerts, ne_eq, + simp only [ConnectedComponent.toSimpleGraph, deleteUniversalVerts, universalVerts, 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 + obtain ⟨c, hc⟩ : ∃ (c : V), (a : V) ≠ c ∧ ¬ Gmax.Adj a c := by + simpa [universalVerts, IsUniversal] using a.1.2.2 + have hbnec : b.val.val ≠ c := by rintro rfl; exact hc.2 hxb obtain ⟨_, hG1⟩ := hMaximal _ <| left_lt_sup.mpr (by rw [edge_le_iff (v := x.1.1) (w := b.1.1)] simp [hnadjxb, Subtype.val_injective.ne <| Subtype.val_injective.ne hnxb]) obtain ⟨_, hG2⟩ := hMaximal _ <| left_lt_sup.mpr (by - rwa [edge_le_iff (v := a.1.1) (w := c), adj_comm, not_or]) - have hcnex : c ≠ x.val.val := by rintro rfl; exact hc.2 hxa + rwa [edge_le_iff (v := a.1.1) (w := c), not_or]) + have hcnex : x.val.val ≠ c := by rintro rfl; exact hc.2 hxa.symm obtain ⟨Mcon, hMcon⟩ := tutte_exists_isPerfectMatching_of_near_matchings hxa - hxb hnadjxb (fun hadj ↦ hc.2 hadj.symm) (by lia) hcnex.symm hc.1 hbnec hG1 hG2 + hxb hnadjxb (fun hadj ↦ hc.2 hadj) (by lia) hcnex hc.1 hbnec hG1 hG2 exact hMatchingFree Mcon hMcon /-- **Tutte's theorem** diff --git a/Mathlib/Combinatorics/SimpleGraph/UniversalVerts.lean b/Mathlib/Combinatorics/SimpleGraph/UniversalVerts.lean index 2cd61fb77f16f0..79107f325d24c5 100644 --- a/Mathlib/Combinatorics/SimpleGraph/UniversalVerts.lean +++ b/Mathlib/Combinatorics/SimpleGraph/UniversalVerts.lean @@ -33,10 +33,10 @@ variable {V : Type*} {G : SimpleGraph V} /-- The set of vertices that are connected to all other vertices. -/ -def universalVerts (G : SimpleGraph V) : Set V := {v : V | ∀ ⦃w⦄, v ≠ w → G.Adj w v} +def universalVerts (G : SimpleGraph V) : Set V := {v : V | G.IsUniversal v} lemma isClique_universalVerts (G : SimpleGraph V) : G.IsClique G.universalVerts := - fun _ _ _ hy hxy ↦ hy hxy.symm + fun _ hx _ _ hxy ↦ hx hxy /-- The subgraph of `G` with the universal vertices removed. @@ -53,7 +53,7 @@ lemma Subgraph.IsMatching.exists_of_universalVerts [Finite V] {s : Set V} obtain ⟨f⟩ : Nonempty (s ≃ t) := by rw [← Cardinal.eq, ← t.cast_ncard t.toFinite, ← s.cast_ncard s.toFinite, ht.2] letI hd := Set.disjoint_of_subset_left ht.1 h - have hadj (v : s) : G.Adj v (f v) := ht.1 (f v).2 (hd.ne_of_mem (f v).2 v.2) + have hadj (v : s) : G.Adj v (f v) := ht.1 (f v).2 (hd.ne_of_mem (f v).2 v.2) |>.symm exact Subgraph.IsMatching.exists_of_disjoint_sets_of_equiv hd.symm f hadj lemma disjoint_image_val_universalVerts (s : Set G.deleteUniversalVerts.verts) : diff --git a/Mathlib/Computability/Encoding.lean b/Mathlib/Computability/Encoding.lean index 38066ea8ffc561..4cd65ae2e2275e 100644 --- a/Mathlib/Computability/Encoding.lean +++ b/Mathlib/Computability/Encoding.lean @@ -14,23 +14,22 @@ public import Mathlib.Tactic.DeriveFintype /-! # Encodings -This file contains the definition of a (finite) encoding, a map from a type to +This file contains the definition of an encoding, a map from a type to strings in an alphabet, used in defining computability by Turing machines. It also contains several examples: ## Examples -- `finEncodingNatBool` : a binary encoding of `ℕ` in a simple alphabet. -- `finEncodingNatΓ'` : a binary encoding of `ℕ` in the alphabet used for TM's. -- `unaryFinEncodingNat` : a unary encoding of `ℕ` -- `finEncodingBoolBool` : an encoding of `Bool`. -- `finEncodingList` : an encoding of `List α` in the alphabet `α`. -- `finEncodingPair` : an encoding of `α × β` from encodings of `α` and `β`. +- `encodingNatBool` : a binary encoding of `ℕ` in a simple alphabet. +- `encodingNatΓ'` : a binary encoding of `ℕ` in the alphabet used for TM's. +- `unaryEncodingNat` : a unary encoding of `ℕ` +- `encodingBoolBool` : an encoding of `Bool`. +- `encodingList` : an encoding of `List α` in the alphabet `α`. +- `encodingProd` : an encoding of `α × β` from encodings of `α` and `β`. -/ @[expose] public section - universe u v open Cardinal @@ -38,9 +37,7 @@ open Cardinal namespace Computability /-- An encoding of a type in a certain alphabet, together with a decoding. -/ -structure Encoding (α : Type u) where - /-- The alphabet of the encoding -/ - Γ : Type v +structure Encoding (α : Type u) (Γ : Type v) where /-- The encoding function -/ encode : α → List Γ /-- The decoding function -/ @@ -50,18 +47,10 @@ structure Encoding (α : Type u) where attribute [simp] Encoding.decode_encode -theorem Encoding.encode_injective {α : Type u} (e : Encoding α) : Function.Injective e.encode := by +theorem Encoding.encode_injective {α Γ} (e : Encoding α Γ) : Function.Injective e.encode := by refine fun _ _ h => Option.some_injective _ ?_ rw [← e.decode_encode, ← e.decode_encode, h] -/-- An `Encoding` plus a guarantee of finiteness of the alphabet. -/ -structure FinEncoding (α : Type u) extends Encoding.{u, 0} α where - /-- The alphabet of the encoding is finite -/ - ΓFin : Fintype Γ - -instance Γ.fintype {α : Type u} (e : FinEncoding α) : Fintype e.toEncoding.Γ := - e.ΓFin - /-- A standard Turing machine alphabet, consisting of blank,bit0,bit1,bra,ket,comma. -/ inductive Γ' | blank @@ -141,27 +130,17 @@ theorem encodePosNum_nonempty (n : PosNum) : encodePosNum n ≠ [] := exact congr_arg ((↑) : Num → ℕ) (decode_encodeNum n) /-- A binary `Encoding` of `ℕ` in `Bool`. -/ -def encodingNatBool : Encoding ℕ where - Γ := Bool +def encodingNatBool : Encoding ℕ Bool where encode := encodeNat decode n := some (decodeNat n) decode_encode n := congr_arg _ (decode_encodeNat n) -/-- A binary encoding of `ℕ` in `Bool`, as a `FinEncoding`. -/ -def finEncodingNatBool : FinEncoding ℕ := - ⟨encodingNatBool, Bool.fintype⟩ - /-- A binary `Encoding` of `ℕ` in `Γ'`. -/ -def encodingNatΓ' : Encoding ℕ where - Γ := Γ' +def encodingNatΓ' : Encoding ℕ Γ' where encode x := List.map inclusionBoolΓ' (encodeNat x) decode x := some (decodeNat (List.map sectionΓ'Bool x)) decode_encode x := congr_arg _ <| by simp [Function.comp_def] -/-- A binary `FinEncoding` of `ℕ` in `Γ'`. -/ -def finEncodingNatΓ' : FinEncoding ℕ := - ⟨encodingNatΓ', inferInstanceAs (Fintype Γ')⟩ - /-- A unary encoding function of `ℕ` in `Bool`. -/ def unaryEncodeNat : Nat → List Bool | 0 => [] @@ -174,13 +153,11 @@ def unaryDecodeNat : List Bool → Nat := @[simp] theorem unary_decode_encode_nat : ∀ n, unaryDecodeNat (unaryEncodeNat n) = n := fun n => Nat.rec rfl (fun (_m : ℕ) hm => (congr_arg Nat.succ hm.symm).symm) n -/-- A unary `FinEncoding` of `ℕ` in `Bool`. -/ -def unaryFinEncodingNat : FinEncoding ℕ where - Γ := Bool +/-- A unary `Encoding` of `ℕ` in `Bool`. -/ +def unaryEncodingNat : Encoding ℕ Bool where encode := unaryEncodeNat decode n := some (unaryDecodeNat n) decode_encode n := congr_arg _ (unary_decode_encode_nat n) - ΓFin := Bool.fintype /-- An encoding function of `Bool` in `Bool`. -/ def encodeBool : Bool → List Bool := pure @@ -192,53 +169,98 @@ def decodeBool : List Bool → Bool @[simp] theorem decode_encodeBool (b : Bool) : decodeBool (encodeBool b) = b := rfl -/-- A `FinEncoding` of `Bool` in `Bool`. -/ -def finEncodingBoolBool : FinEncoding Bool where - Γ := Bool +/-- An `Encoding` of `Bool` in `Bool`. -/ +def encodingBoolBool : Encoding Bool Bool where encode := encodeBool decode x := some (decodeBool x) decode_encode x := congr_arg _ (decode_encodeBool x) - ΓFin := Bool.fintype - -instance inhabitedFinEncoding : Inhabited (FinEncoding Bool) := - ⟨finEncodingBoolBool⟩ -instance inhabitedEncoding : Inhabited (Encoding Bool) := - ⟨finEncodingBoolBool.toEncoding⟩ +instance inhabitedEncoding : Inhabited (Encoding Bool Bool) := + ⟨encodingBoolBool⟩ -theorem Encoding.card_le_card_list {α : Type u} (e : Encoding.{u, v} α) : - Cardinal.lift.{v} #α ≤ Cardinal.lift.{u} #(List e.Γ) := +theorem Encoding.card_le_card_list {α : Type u} {Γ : Type v} (e : Encoding α Γ) : + Cardinal.lift.{v} #α ≤ Cardinal.lift.{u} #(List Γ) := Cardinal.lift_mk_le'.2 ⟨⟨e.encode, e.encode_injective⟩⟩ -theorem Encoding.card_le_aleph0 {α : Type u} (e : Encoding.{u, v} α) [Countable e.Γ] : +theorem Encoding.card_le_aleph0 {α Γ} (e : Encoding α Γ) [Countable Γ] : #α ≤ ℵ₀ := haveI : Countable α := e.encode_injective.countable Cardinal.mk_le_aleph0 -theorem FinEncoding.card_le_aleph0 {α : Type u} (e : FinEncoding α) : #α ≤ ℵ₀ := - e.toEncoding.card_le_aleph0 - -/-- A `FinEncoding` of a `List α` in (finite) alphabet `α`, encoded directly. -/ -def finEncodingList (α : Type) [Fintype α] : FinEncoding (List α) where - Γ := α +/-- An `Encoding` of a `List α` in alphabet `α`, encoded directly. -/ +def encodingList (α : Type) : Encoding (List α) α where encode := id decode := Option.some decode_encode _ := rfl - ΓFin := inferInstance /-- -Given `FinEncoding` of `α` and `β`, -constructs a `FinEncoding` of `α × β` by concatenating the encodings, +Given an `Encoding` of `α` and `β`, +constructs an `Encoding` of `α × β` by concatenating the encodings, mapping the symbols from the first encoding with `Sum.inl` and those from the second with `Sum.inr`. -/ -def finEncodingPair {α β : Type*} (ea : FinEncoding α) (eb : FinEncoding β) : - FinEncoding (α × β) where - Γ := ea.Γ ⊕ eb.Γ +def encodingProd {α β Γ₁ Γ₂ : Type*} (ea : Encoding α Γ₁) (eb : Encoding β Γ₂) : + Encoding (α × β) (Γ₁ ⊕ Γ₂) where encode x := (ea.encode x.1).map .inl ++ (eb.encode x.2).map .inr decode x := Option.map₂ Prod.mk (ea.decode (x.filterMap Sum.getLeft?)) (eb.decode (x.filterMap Sum.getRight?)) decode_encode x := by simp - ΓFin := inferInstance + +/-! ### Deprecated aliases for `FinEncoding` and unbundled `Γ` -/ + +/-- Deprecated: Use `Encoding α Γ` along with `[Fintype Γ]` instead. -/ +@[reducible, nolint unusedArguments, + deprecated "Use `Encoding α Γ` along with `[Fintype Γ]` instead" (since := "2026-05-07")] +def FinEncoding (α : Type u) {Γ : Type v} [Fintype Γ] := Encoding α Γ + +/-- Deprecated: `Γ` is now an explicit parameter of `Encoding`. -/ +@[reducible, nolint unusedArguments, + deprecated "Γ is now an explicit parameter of `Encoding`" (since := "2026-05-07")] +def Encoding.Γ {α : Type u} {Γ : Type v} (_ : Encoding α Γ) : Type v := Γ + +/-- Deprecated: Use `inferInstanceAs (Fintype Γ)` instead. -/ +@[reducible, nolint unusedArguments, + deprecated "Use `inferInstanceAs (Fintype Γ)` instead" (since := "2026-05-07")] +def FinEncoding.ΓFin {α : Type u} {Γ : Type v} [h : Fintype Γ] + (_ : Encoding α Γ) : Fintype Γ := h + +/-- Deprecated: Use the encoding directly. -/ +@[reducible, nolint unusedArguments, + deprecated "Use the encoding directly" (since := "2026-05-07")] +def FinEncoding.toEncoding {α : Type u} {Γ : Type v} [Fintype Γ] + (e : Encoding α Γ) : Encoding α Γ := e + +/-- Deprecated alias for `encodingNatBool`. -/ +@[deprecated encodingNatBool (since := "2026-05-07")] +abbrev finEncodingNatBool := encodingNatBool + +/-- Deprecated alias for `encodingNatΓ'`. -/ +@[deprecated encodingNatΓ' (since := "2026-05-07")] +abbrev finEncodingNatΓ' := encodingNatΓ' + +/-- Deprecated alias for `unaryEncodingNat`. -/ +@[deprecated unaryEncodingNat (since := "2026-05-07")] +abbrev unaryFinEncodingNat := unaryEncodingNat + +/-- Deprecated alias for `encodingBoolBool`. -/ +@[deprecated encodingBoolBool (since := "2026-05-07")] +abbrev finEncodingBoolBool := encodingBoolBool + +/-- Deprecated alias for `encodingList`. -/ +@[reducible, nolint unusedArguments, + deprecated encodingList (since := "2026-05-07")] +def finEncodingList (α : Type) [Fintype α] := encodingList α + +/-- Deprecated alias for `encodingProd`. -/ +@[reducible, nolint unusedArguments, + deprecated encodingProd (since := "2026-05-07")] +def finEncodingPair {α β Γ₁ Γ₂ : Type*} [Fintype Γ₁] [Fintype Γ₂] + (ea : Encoding α Γ₁) (eb : Encoding β Γ₂) := + encodingProd ea eb + +/-- Deprecated alias for `Encoding.card_le_aleph0`. -/ +@[deprecated Encoding.card_le_aleph0 (since := "2026-05-07")] +theorem FinEncoding.card_le_aleph0 {α Γ} [Countable Γ] (e : Encoding α Γ) : #α ≤ ℵ₀ := + e.card_le_aleph0 end Computability diff --git a/Mathlib/Computability/Partrec.lean b/Mathlib/Computability/Partrec.lean index f73cb614714702..0e6f71a84e1bc8 100644 --- a/Mathlib/Computability/Partrec.lean +++ b/Mathlib/Computability/Partrec.lean @@ -46,6 +46,7 @@ set_option backward.privateInPublic true in private def lbp (m n : ℕ) : Prop := m = n + 1 ∧ ∀ k ≤ n, false ∈ p k +set_option linter.defProp false in set_option backward.privateInPublic true in private def wf_lbp (H : ∃ n, true ∈ p n ∧ ∀ k < n, (p k).Dom) : WellFounded (lbp p) := ⟨by diff --git a/Mathlib/Computability/TuringMachine/StackTuringMachine.lean b/Mathlib/Computability/TuringMachine/StackTuringMachine.lean index 0e1a5b29264881..b1d70fe140e99a 100644 --- a/Mathlib/Computability/TuringMachine/StackTuringMachine.lean +++ b/Mathlib/Computability/TuringMachine/StackTuringMachine.lean @@ -300,9 +300,9 @@ stacks, but we have only one tape, so we must "multiplex" them all together. Pic 1 contains `[a, b]` and stack 2 contains `[c, d, e, f]` then the tape looks like this: ``` - bottom: ... | _ | T | _ | _ | _ | _ | ... - stack 1: ... | _ | b | a | _ | _ | _ | ... - stack 2: ... | _ | f | e | d | c | _ | ... +bottom: ... | _ | T | _ | _ | _ | _ | ... +stack 1: ... | _ | b | a | _ | _ | _ | ... +stack 2: ... | _ | f | e | d | c | _ | ... ``` where a tape element is a vertical slice through the diagram. Here the alphabet is diff --git a/Mathlib/Condensed/Discrete/Colimit.lean b/Mathlib/Condensed/Discrete/Colimit.lean index 3644a930d60a4c..789a1ec6e5c50d 100644 --- a/Mathlib/Condensed/Discrete/Colimit.lean +++ b/Mathlib/Condensed/Discrete/Colimit.lean @@ -62,6 +62,7 @@ noncomputable def isColimitLocallyConstantPresheaf (hc : IsLimit c) [∀ i, Epi dsimp rwa [dsimp% c.w, dsimp% c.w] +set_option backward.isDefEq.respectTransparency false in @[simp] lemma isColimitLocallyConstantPresheaf_desc_apply (hc : IsLimit c) [∀ i, Epi (c.π.app i)] (s : Cocone ((F ⋙ toProfinite).op ⋙ locallyConstantPresheaf X)) @@ -349,6 +350,7 @@ noncomputable def isColimitLocallyConstantPresheaf (hc : IsLimit c) [∀ i, Epi dsimp rwa [dsimp% c.w, dsimp% c.w] +set_option backward.isDefEq.respectTransparency false in @[simp] lemma isColimitLocallyConstantPresheaf_desc_apply (hc : IsLimit c) [∀ i, Epi (c.π.app i)] (s : Cocone ((F ⋙ toLightProfinite).op ⋙ locallyConstantPresheaf X)) @@ -366,6 +368,7 @@ noncomputable def isColimitLocallyConstantPresheafDiagram (S : LightProfinite) : (Functor.Final.isColimitWhiskerEquiv (opOpEquivalence ℕ).inverse _).symm (isColimitLocallyConstantPresheaf _ _ S.asLimit) +set_option backward.isDefEq.respectTransparency false in @[simp] lemma isColimitLocallyConstantPresheafDiagram_desc_apply (S : LightProfinite) (s : Cocone (S.diagram.rightOp ⋙ locallyConstantPresheaf X)) diff --git a/Mathlib/Condensed/TopComparison.lean b/Mathlib/Condensed/TopComparison.lean index 0fd74643ae8dbf..11985db7efee7a 100644 --- a/Mathlib/Condensed/TopComparison.lean +++ b/Mathlib/Condensed/TopComparison.lean @@ -117,7 +117,7 @@ def TopCat.toSheafCompHausLike : apply +allowSynthFailures equalizerCondition_yonedaPresheaf (CompHausLike.compHausLikeToTop.{u} P) X intro Z B π he - apply IsQuotientMap.of_surjective_continuous (hs _ he) π.hom.hom.continuous + exact .of_surjective_continuous (hs _ he) π.hom.hom.continuous /-- `TopCat.toSheafCompHausLike` yields a functor from `TopCat.{max u w}` to diff --git a/Mathlib/Data/BitVec.lean b/Mathlib/Data/BitVec.lean index c76df5a6a66593..7e2da761f6c3bf 100644 --- a/Mathlib/Data/BitVec.lean +++ b/Mathlib/Data/BitVec.lean @@ -71,6 +71,7 @@ lemma toFin_zsmul (z : ℤ) (x : BitVec w) : toFin (z • x) = z • x.toFin := open scoped Fin.CommRing in simp only [zsmul_eq_mul, toFin_intCast] +set_option backward.isDefEq.respectTransparency false in lemma toFin_pow (x : BitVec w) (n : ℕ) : toFin (x ^ n) = x.toFin ^ n := by induction n with | zero => simp @@ -81,7 +82,7 @@ lemma toFin_pow (x : BitVec w) (n : ℕ) : toFin (x ^ n) = x.toFin ^ n := by -/ -- Verify that the `HPow` instance from Lean agrees definitionally with the instance via `Monoid`. -example : @instHPow (Fin (2 ^ w)) ℕ Monoid.toPow = Lean.Grind.Fin.instHPowFinNatOfNeZero := rfl +example : @instHPow (Fin (2 ^ w)) ℕ NPow.toPow = Lean.Grind.Fin.instHPowFinNatOfNeZero := rfl instance : CommSemiring (BitVec w) := open Fin.CommRing in diff --git a/Mathlib/Data/Bracket.lean b/Mathlib/Data/Bracket.lean index 1468d018300c55..f7a0dc76ffe27a 100644 --- a/Mathlib/Data/Bracket.lean +++ b/Mathlib/Data/Bracket.lean @@ -9,6 +9,7 @@ public import Mathlib.Init /-! # Bracket Notation + This file provides notation which can be used for the Lie bracket, for the commutator of two subgroups, and for other similar operations. diff --git a/Mathlib/Data/Bundle.lean b/Mathlib/Data/Bundle.lean index 55c2b8f33e0929..d5e4ee0e06bc92 100644 --- a/Mathlib/Data/Bundle.lean +++ b/Mathlib/Data/Bundle.lean @@ -9,6 +9,7 @@ public import Mathlib.Data.Set.Basic /-! # Bundle + Basic data structure to implement fiber bundles, vector bundles (maybe fibrations?), etc. This file should contain all possible results that do not involve any topology. diff --git a/Mathlib/Data/Complex/Basic.lean b/Mathlib/Data/Complex/Basic.lean index feb4b396fd8848..12f58340718e32 100644 --- a/Mathlib/Data/Complex/Basic.lean +++ b/Mathlib/Data/Complex/Basic.lean @@ -323,8 +323,6 @@ theorem real_smul {x : ℝ} {z : ℂ} : x • z = x * z := end SMul instance addCommGroup : AddCommGroup ℂ where - nsmul := (· • ·) - zsmul := (· • ·) zsmul_zero' := by intros; ext <;> simp [smul_re, smul_im] nsmul_zero := by intros; ext <;> simp [smul_re, smul_im] nsmul_succ := by intros; ext <;> simp [smul_re, smul_im] <;> ring diff --git a/Mathlib/Data/DFinsupp/Lex.lean b/Mathlib/Data/DFinsupp/Lex.lean index a701e03d918e5a..b2d249782a7be0 100644 --- a/Mathlib/Data/DFinsupp/Lex.lean +++ b/Mathlib/Data/DFinsupp/Lex.lean @@ -105,6 +105,7 @@ instance Colex.isStrictOrder [∀ i, PartialOrder (α i)] : See `DFinsupp.Lex.linearOrder` for a proof that this partial order is in fact linear. -/ instance Lex.partialOrder [∀ i, PartialOrder (α i)] : PartialOrder (Lex (Π₀ i, α i)) where le x y := ⇑(ofLex x) = ⇑(ofLex y) ∨ x < y + toLT := instLTLex __ := PartialOrder.lift (fun x : Lex (Π₀ i, α i) ↦ toLex (⇑(ofLex x))) (DFunLike.coe_injective (F := DFinsupp α)) @@ -112,6 +113,7 @@ instance Lex.partialOrder [∀ i, PartialOrder (α i)] : PartialOrder (Lex (Π See `DFinsupp.Colex.linearOrder` for a proof that this partial order is in fact linear. -/ instance Colex.partialOrder [∀ i, PartialOrder (α i)] : PartialOrder (Colex (Π₀ i, α i)) where le x y := ⇑(ofColex x) = ⇑(ofColex y) ∨ x < y + toLT := instLTColex __ := PartialOrder.lift (fun x : Colex (Π₀ i, α i) ↦ toColex (⇑(ofColex x))) (DFunLike.coe_injective (F := DFinsupp α)) @@ -175,14 +177,12 @@ instance Colex.decidableLT : DecidableLT (Colex (Π₀ i, α i)) := /-- The linear order on `DFinsupp`s obtained by the lexicographic ordering. -/ instance Lex.linearOrder : LinearOrder (Lex (Π₀ i, α i)) where - __ := Lex.partialOrder le_total := total_of _ toDecidableLT := decidableLT toDecidableLE := decidableLE /-- The linear order on `DFinsupp`s obtained by the colexicographic ordering. -/ instance Colex.linearOrder : LinearOrder (Colex (Π₀ i, α i)) where - __ := Colex.partialOrder le_total := total_of _ toDecidableLT := decidableLT toDecidableLE := decidableLE diff --git a/Mathlib/Data/ENNReal/Basic.lean b/Mathlib/Data/ENNReal/Basic.lean index 3b71f90289ab75..218392836d7fad 100644 --- a/Mathlib/Data/ENNReal/Basic.lean +++ b/Mathlib/Data/ENNReal/Basic.lean @@ -191,6 +191,12 @@ instance : Inhabited ℝ≥0∞ := ⟨0⟩ def recTopCoe {C : ℝ≥0∞ → Sort*} (top : C ∞) (coe : ∀ x : ℝ≥0, C x) (x : ℝ≥0∞) : C x := WithTop.recTopCoe top coe x +@[simp] lemma recTopCoe_top {C : ℝ≥0∞ → Sort*} (top : C ∞) (coe : ∀ x : ℝ≥0, C x) : + recTopCoe top coe ∞ = top := rfl + +@[simp] lemma recTopCoe_ofNNReal {C : ℝ≥0∞ → Sort*} (top : C ∞) (coe : ∀ x : ℝ≥0, C x) (x : ℝ≥0) : + recTopCoe top coe x = coe x := rfl + instance canLift : CanLift ℝ≥0∞ ℝ≥0 ofNNReal (· ≠ ∞) := WithTop.canLift @[simp] theorem none_eq_top : (none : ℝ≥0∞) = ∞ := rfl @@ -741,8 +747,8 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: `ENNReal.toReal`. -/ @[positivity ENNReal.toReal _] -meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(ENNReal.toReal $a) => assertInstancesCommute @@ -751,8 +757,8 @@ meta def evalENNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for the `positivity` tactic: `ENNReal.ofNNReal`. -/ @[positivity ENNReal.ofNNReal _] -meta def evalENNRealOfNNReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalENNRealOfNNReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ≥0∞), ~q(ENNReal.ofNNReal $a) => assertInstancesCommute diff --git a/Mathlib/Data/ENNReal/Real.lean b/Mathlib/Data/ENNReal/Real.lean index 6da5409e3594ec..2aeca7a1b8dabf 100644 --- a/Mathlib/Data/ENNReal/Real.lean +++ b/Mathlib/Data/ENNReal/Real.lean @@ -392,8 +392,8 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: `ENNReal.ofReal`. -/ @[positivity ENNReal.ofReal _] -meta def evalENNRealOfReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalENNRealOfReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ≥0∞), ~q(ENNReal.ofReal $a) => assertInstancesCommute diff --git a/Mathlib/Data/EReal/Basic.lean b/Mathlib/Data/EReal/Basic.lean index 9cbb2d70f53709..2e8a1d014a1c2c 100644 --- a/Mathlib/Data/EReal/Basic.lean +++ b/Mathlib/Data/EReal/Basic.lean @@ -850,8 +850,8 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: cast from `ℝ` to `EReal`. -/ @[positivity Real.toEReal _] -meta def evalRealToEReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalRealToEReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(EReal), ~q(Real.toEReal $a) => assertInstancesCommute @@ -865,8 +865,8 @@ meta def evalRealToEReal : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for the `positivity` tactic: cast from `ℝ≥0∞` to `EReal`. -/ @[positivity ENNReal.toEReal _] -meta def evalENNRealToEReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalENNRealToEReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(EReal), ~q(ENNReal.toEReal $a) => assertInstancesCommute @@ -883,8 +883,8 @@ We prove that `EReal.toReal x` is nonnegative whenever `x` is nonnegative. Since `EReal.toReal ⊤ = 0`, we cannot prove a stronger statement, at least without relying on a tactic like `finiteness`. -/ @[positivity EReal.toReal _] -meta def evalERealToReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalERealToReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(Real), ~q(EReal.toReal $a) => assertInstancesCommute @@ -900,8 +900,8 @@ and it is nonnegative otherwise. We cannot deduce any corollaries from `x ≠ 0`, since `EReal.toENNReal x = 0` for `x < 0`. -/ @[positivity EReal.toENNReal _] -meta def evalERealToENNReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalERealToENNReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ENNReal), ~q(EReal.toENNReal $a) => assertInstancesCommute diff --git a/Mathlib/Data/EReal/Inv.lean b/Mathlib/Data/EReal/Inv.lean index e61ad93ab0efa8..20f2c4bbc6a20c 100644 --- a/Mathlib/Data/EReal/Inv.lean +++ b/Mathlib/Data/EReal/Inv.lean @@ -551,8 +551,8 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: inverse of an `EReal`. -/ @[positivity (_⁻¹ : EReal)] -meta def evalERealInv : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalERealInv : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match u, α, e with | 0, ~q(EReal), ~q($a⁻¹) => assertInstancesCommute @@ -563,8 +563,8 @@ meta def evalERealInv : PositivityExt where eval {u α} zα pα? e := do /-- Extension for the `positivity` tactic: ratio of two `EReal`s. -/ @[positivity (_ / _ : EReal)] -meta def evalERealDiv : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalERealDiv : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match u, α, e with | 0, ~q(EReal), ~q($a / $b) => assertInstancesCommute diff --git a/Mathlib/Data/EReal/Operations.lean b/Mathlib/Data/EReal/Operations.lean index 075d47a6994978..da8c1992221660 100644 --- a/Mathlib/Data/EReal/Operations.lean +++ b/Mathlib/Data/EReal/Operations.lean @@ -419,6 +419,11 @@ lemma toENNReal_sub {x y : EReal} (hy : 0 ≤ y) : ofReal_sub x (EReal.coe_nonneg.mp hy)] simp +lemma add_sub_add_comm {a b c d : EReal} (h1 : c ≠ ⊥ ∨ d ≠ ⊤) (h2 : c ≠ ⊤ ∨ d ≠ ⊥) : + a + b - (c + d) = (a - c) + (b - d) := by + rw [sub_eq_add_neg, sub_eq_add_neg, sub_eq_add_neg, EReal.neg_add h1 h2, sub_eq_add_neg] + grind + lemma add_sub_cancel_right {a : EReal} {b : Real} : a + b - b = a := by cases a <;> norm_cast exact _root_.add_sub_cancel_right _ _ @@ -488,6 +493,27 @@ lemma sub_lt_of_lt_add {a b c : EReal} (h : a < b + c) : a - c < b := lemma sub_lt_of_lt_add' {a b c : EReal} (h : a < b + c) : a - b < c := sub_lt_of_lt_add <| by rwa [add_comm] +lemma sub_lt_sub_of_le_of_gt {x y z t : EReal} (h : x ≤ y) (h' : z < t) + (hx_top : x ≠ ⊤) (hy_bot : y ≠ ⊥) : + x - t < y - z := by + refine sub_lt_of_lt_add' ?_ + rw [add_sub_assoc', add_comm, add_sub_assoc] + by_cases hy_top : y = ⊤ + · rw [hy_top, top_add_of_ne_bot] + · exact hx_top.lt_top + · exact ne_bot_of_le_ne_bot (by simp) (sub_pos.mpr h').le + by_cases hxy : x = y + · rw [hxy] + lift y to ℝ using ⟨hy_top, hy_bot⟩ + by_cases htz_top : t - z = ⊤ + · simp_all + rw [← coe_toReal htz_top <| ne_bot_of_le_ne_bot (by simp) (sub_pos.mpr h').le] + norm_cast + refine lt_add_of_pos_right y ?_ + exact EReal.toReal_pos (sub_pos.mpr h') htz_top + · rw [← add_zero x] + exact add_lt_add (by grind) (sub_pos.mpr h') + /-! ### Addition and order -/ lemma le_of_forall_lt_iff_le {x y : EReal} : (∀ z : ℝ, x < z → y ≤ z) ↔ y ≤ x := by @@ -790,6 +816,11 @@ lemma left_distrib_of_nonneg {a b c : EReal} (ha : 0 ≤ a) (hb : 0 ≤ b) : nth_rewrite 1 [EReal.mul_comm]; nth_rewrite 2 [EReal.mul_comm]; nth_rewrite 3 [EReal.mul_comm] exact right_distrib_of_nonneg ha hb +lemma mul_sub_of_nonneg_of_nonpos {a b c : EReal} (hb : 0 ≤ b) (hc : c ≤ 0) : + a * (b - c) = a * b - a * c := by + rw [sub_eq_add_neg, left_distrib_of_nonneg hb (by simpa)] + simp [← neg_mul, sub_eq_add_neg] + lemma left_distrib_of_nonneg_of_ne_top {x : EReal} (hx_nonneg : 0 ≤ x) (hx_ne_top : x ≠ ⊤) (y z : EReal) : x * (y + z) = x * y + x * z := by @@ -805,6 +836,16 @@ lemma right_distrib_of_nonneg_of_ne_top {x : EReal} (hx_nonneg : 0 ≤ x) (y + z) * x = y * x + z * x := by simpa only [EReal.mul_comm] using left_distrib_of_nonneg_of_ne_top hx_nonneg hx_ne_top y z +lemma mul_sub_of_nonneg_of_ne_top {a b c : EReal} (ha : 0 ≤ a) (ha' : a ≠ ⊤) : + a * (b - c) = a * b - a * c := by + rw [sub_eq_add_neg, left_distrib_of_nonneg_of_ne_top ha ha'] + simp [← neg_mul, sub_eq_add_neg] + +lemma sub_mul_of_nonneg_of_ne_top {a b c : EReal} (ha : 0 ≤ a) (ha' : a ≠ ⊤) : + (b - c) * a = b * a - c * a := by + rw [sub_eq_add_neg, right_distrib_of_nonneg_of_ne_top ha ha'] + simp [← neg_mul, sub_eq_add_neg] + @[simp] lemma nsmul_eq_mul (n : ℕ) (x : EReal) : n • x = n * x := by induction n with @@ -821,8 +862,8 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: sum of two `EReal`s. -/ @[positivity (_ + _ : EReal)] -meta def evalERealAdd : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalERealAdd : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match u, α, e with | 0, ~q(EReal), ~q($a + $b) => assertInstancesCommute @@ -841,8 +882,8 @@ meta def evalERealAdd : PositivityExt where eval {u α} zα pα? e := do /-- Extension for the `positivity` tactic: product of two `EReal`s. -/ @[positivity (_ * _ : EReal)] -meta def evalERealMul : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalERealMul : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match u, α, e with | 0, ~q(EReal), ~q($a * $b) => assertInstancesCommute diff --git a/Mathlib/Data/Fin/Basic.lean b/Mathlib/Data/Fin/Basic.lean index e5e643bffb4319..344ac23ae0b0e3 100644 --- a/Mathlib/Data/Fin/Basic.lean +++ b/Mathlib/Data/Fin/Basic.lean @@ -84,6 +84,21 @@ lemma ne_zero_of_lt {a b : Fin (n + 1)} (hab : a < b) : b ≠ 0 := lemma ne_last_of_lt {a b : Fin (n + 1)} (hab : a < b) : a ≠ last n := Fin.ne_of_lt <| Fin.lt_of_lt_of_le hab b.le_last +lemma ne_last_of_ne_last_of_le {a b : Fin (n + 1)} (hb : b ≠ last n) (hab : a ≤ b) : + a ≠ last n := by + intro rfl + exact Nat.not_lt_of_le hab (lt_last_iff_ne_last.mpr hb) + +lemma val_sub_lt_of_lt_of_le {a b : Fin n} (ha : a.val < m) (hab : b ≤ a) : + (a - b).val < m := by + rw [Fin.sub_val_of_le hab] + exact sub_lt_of_lt ha + +lemma sub_ne_last_of_ne_last_of_le {a b : Fin (n + 1)} (ha : a ≠ last n) (hab : b ≤ a) : + a - b ≠ last n := by + rw [← lt_last_iff_ne_last, lt_def] + exact val_sub_lt_of_lt_of_le (val_lt_last ha) hab + /-- Equivalence between `Fin n` and `{ i // i < n }`. -/ @[simps apply symm_apply] def equivSubtype : Fin n ≃ { i // i < n } where diff --git a/Mathlib/Data/Fin/SuccPred.lean b/Mathlib/Data/Fin/SuccPred.lean index e3672dc3d438d9..99d0eb5599a338 100644 --- a/Mathlib/Data/Fin/SuccPred.lean +++ b/Mathlib/Data/Fin/SuccPred.lean @@ -264,6 +264,13 @@ theorem succ_castAdd (i : Fin n) : succ (castAdd m i) = theorem succ_natAdd (i : Fin m) : succ (natAdd n i) = natAdd n (succ i) := rfl +theorem sub_castAdd_eq_castAdd_sub_of_le {n : ℕ} {a b : Fin n} (h : b ≤ a) : + a.castAdd m - b.castAdd m = (a - b).castAdd m := by + grind [Fin.sub_val_of_le] + +theorem sub_castSucc_eq_castSucc_sub_of_le {n : ℕ} {a b : Fin n} (h : b ≤ a) : + a.castSucc - b.castSucc = (a - b).castSucc := sub_castAdd_eq_castAdd_sub_of_le h + end Succ section Pred @@ -457,6 +464,42 @@ theorem pred_lt_castPred {a : Fin (n + 1)} (h₁ : a ≠ 0) (h₂ : a ≠ last n pred a h₁ < castPred a h₂ := by rw [pred_lt_castPred_iff, le_def] +theorem val_sub_castLT_of_le {a b : Fin m} (ha : a.val < n) (h : b ≤ a) : + (a.castLT ha - b.castLT (lt_of_le_of_lt h ha)).val = (a - b).val := by + have : b.castLT (lt_of_le_of_lt h ha) ≤ a.castLT ha := by simpa [← val_fin_le] using h + simp [sub_val_of_le, h, this] + +theorem sub_castLT_eq_castLT_sub_of_le {a b : Fin m} (ha : a.val < n) (h : b ≤ a) : + a.castLT ha - b.castLT (lt_of_le_of_lt h ha) = + (a - b).castLT (val_sub_lt_of_lt_of_le ha h) := by + rw [Fin.ext_iff] + exact val_sub_castLT_of_le ha h + +theorem val_sub_castLT_of_lt {a b : Fin m} (hb : b < n) (h : a < b) : + (a.castLT (lt_trans h hb) - b.castLT hb).val = (a - b).val + n - m := by + simp only [val_sub, val_castLT] + repeat rw [Nat.mod_eq_of_lt (by omega)] + have h' : a.val < b.val := h + omega + +theorem val_sub_castPred_of_le {a b : Fin (n + 1)} (ha : a ≠ last n) + (h : b ≤ a) : + (a.castPred ha - b.castPred (ne_last_of_ne_last_of_le ha h)).val = (a - b).val := + val_sub_castLT_of_le (lt_last_iff_ne_last.mpr ha) h + +theorem sub_castPred_eq_castPred_sub_of_le {a b : Fin (n + 1)} (ha : a ≠ last n) + (h : b ≤ a) : + a.castPred ha - b.castPred (ne_last_of_ne_last_of_le ha h) = + (a - b).castPred (sub_ne_last_of_ne_last_of_le ha h) := + sub_castLT_eq_castLT_sub_of_le (lt_last_iff_ne_last.mpr ha) h + +theorem val_sub_castPred_of_ge {a b : Fin (n + 1)} (hb : b ≠ last n) + (h : a ≤ b) : + (a.castPred (ne_last_of_ne_last_of_le hb h) - b.castPred hb).val = (a - b).val - 1 := by + obtain (rfl | h') := Fin.eq_or_lt_of_le h + · simp [val_sub, Nat.sub_add_cancel a.is_le] + grind [castPred, val_sub_castLT_of_lt] + end CastPred section SuccAbove diff --git a/Mathlib/Data/Fin/Tuple/Basic.lean b/Mathlib/Data/Fin/Tuple/Basic.lean index cc05c5f49d6741..4ec26a5bfc822b 100644 --- a/Mathlib/Data/Fin/Tuple/Basic.lean +++ b/Mathlib/Data/Fin/Tuple/Basic.lean @@ -1183,7 +1183,7 @@ lemma find_congr (hi : p i) (hpq : ∀ j ≤ i, p j ↔ q j) : /-- A weak version of `Fin.find_congr`, requiring `p = q` everywhere. -/ lemma find_congr' {hp : ∃ i, p i} {hq : ∃ i, q i} (hpq : ∀ {i}, p i ↔ q i) : - Fin.find p hp = Fin.find q hq := + Fin.find p hp = Fin.find q hq := let ⟨_, hp⟩ := hp; find_congr hp fun _ _ ↦ hpq lemma find_le (hi : p i) : Fin.find p ⟨i, hi⟩ ≤ i := diff --git a/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean b/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean index 980e2e2030d76d..9a7d516485437b 100644 --- a/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean +++ b/Mathlib/Data/Fin/Tuple/NatAntidiagonal.lean @@ -234,7 +234,7 @@ section EquivProd /-- The disjoint union of antidiagonal tuples `Σ n, antidiagonalTuple k n` is equivalent to the `k`-tuple `Fin k → ℕ`. This is such an equivalence, obtained by mapping `(n, x)` to `x`. -This is the tuple version of `Finset.sigmaAntidiagonalEquivProd`. -/ +This is the tuple version of `Finset..HasAntidiagonal.sigmaAntidiagonalEquivProd`. -/ @[simps] def sigmaAntidiagonalTupleEquivTuple (k : ℕ) : (Σ n, antidiagonalTuple k n) ≃ (Fin k → ℕ) where toFun x := x.2 diff --git a/Mathlib/Data/Fin/Tuple/Reflection.lean b/Mathlib/Data/Fin/Tuple/Reflection.lean index 0b5d508e05e0b9..e577f7abd086a9 100644 --- a/Mathlib/Data/Fin/Tuple/Reflection.lean +++ b/Mathlib/Data/Fin/Tuple/Reflection.lean @@ -175,10 +175,10 @@ open Lean Meta Qq that shows it is equal to `∏ i, f i`. -/ meta def mkProdEqQ {u : Level} {α : Q(Type u)} (inst : Q(CommMonoid $α)) (n : ℕ) (f : Q(Fin $n → $α)) : - MetaM <| (val : Q($α)) × Q(∏ i, $f i = $val) := do + MetaM <| (val : Q($α)) × Q(∏ i, $f i = $val) := match n with - | 0 => return ⟨q((1 : $α)), q(Fin.prod_univ_zero $f)⟩ - | m + 1 => + | 0 => do return ⟨q((1 : $α)), q(Fin.prod_univ_zero $f)⟩ + | m + 1 => do let nezero : Q(NeZero ($m + 1)) := q(⟨Nat.succ_ne_zero _⟩) let val ← makeRHS (m + 1) f nezero (m + 1) let _ : $val =Q FinVec.prod $f := ⟨⟩ @@ -198,10 +198,10 @@ where that shows it is equal to `∑ i, f i`. -/ meta def mkSumEqQ {u : Level} {α : Q(Type u)} (inst : Q(AddCommMonoid $α)) (n : ℕ) (f : Q(Fin $n → $α)) : - MetaM <| (val : Q($α)) × Q(∑ i, $f i = $val) := do + MetaM <| (val : Q($α)) × Q(∑ i, $f i = $val) := match n with | 0 => return ⟨q((0 : $α)), q(Fin.sum_univ_zero $f)⟩ - | m + 1 => + | m + 1 => do let nezero : Q(NeZero ($m + 1)) := q(⟨Nat.succ_ne_zero _⟩) let val ← makeRHS (m + 1) f nezero (m + 1) let _ : $val =Q FinVec.sum $f := ⟨⟩ @@ -228,7 +228,7 @@ open Qq Lean FinVec simproc_decl prod_univ_ofNat (∏ _ : Fin _, _) := .ofQ fun u _ e => do match u, e with | .succ _, ~q(@Finset.prod (Fin $n) _ $inst (@Finset.univ _ $instF) $f) => do - match (generalizing := false) n.nat? with + match n.nat? with | none => return .continue | some nVal => diff --git a/Mathlib/Data/Finset/Card.lean b/Mathlib/Data/Finset/Card.lean index 2fd5eac026ce27..cc5329230c10c2 100644 --- a/Mathlib/Data/Finset/Card.lean +++ b/Mathlib/Data/Finset/Card.lean @@ -680,6 +680,9 @@ theorem card_eq_one : #s = 1 ↔ ∃ a, s = {a} := by cases s simp only [Multiset.card_eq_one, Finset.card, ← val_inj, singleton_val] +theorem card_eq_one_iff_existsUnique : #s = 1 ↔ ∃! a, a ∈ s := by + simp [card_eq_one, Finset.singleton_iff_unique_mem] + theorem exists_eq_insert_iff [DecidableEq α] : (∃ a ∉ s, insert a s = t) ↔ s ⊆ t ∧ #s + 1 = #t := by constructor @@ -738,6 +741,16 @@ theorem one_lt_card_iff_nontrivial : 1 < #s ↔ s.Nontrivial := by rw [← not_iff_not, not_lt, Finset.Nontrivial, ← Set.nontrivial_coe_sort, not_nontrivial_iff_subsingleton, card_le_one_iff_subsingleton_coe, coe_sort_coe] +/-- Given an injective map `f : α → β` for finite sets `s ⊂ α` and `t ⊂ β` such that `t` has + cardinality one more than `s`, there exists a unique element of `t` not in `f(s)`. -/ +theorem existsUnique_notMem_image_of_injOn_of_card_eq_add_one + {t : Finset β} [DecidableEq β] + (hf : Set.InjOn f s) (hf' : Set.MapsTo f s t) (h : #t = #s + 1) : + ∃! x, x ∈ t ∧ x ∉ s.image f := by + have : #(t \ s.image f) = 1 := by + grind [card_sdiff_of_subset hf'.finsetImage_subset, card_image_of_injOn hf] + simpa [card_eq_one_iff_existsUnique] using this + /-- If a Finset in a Pi type is nontrivial (has at least two elements), then its projection to some factor is nontrivial, and the fibers of the projection are proper subsets. -/ diff --git a/Mathlib/Data/Finset/Empty.lean b/Mathlib/Data/Finset/Empty.lean index 3064902c8f0a7a..d6c6804b82af3e 100644 --- a/Mathlib/Data/Finset/Empty.lean +++ b/Mathlib/Data/Finset/Empty.lean @@ -74,6 +74,10 @@ theorem Nonempty.forall_const {s : Finset α} (h : s.Nonempty) {p : Prop} : (∀ let ⟨x, hx⟩ := h ⟨fun h => h x hx, fun h _ _ => h⟩ +@[simp] +theorem forall_mem_const {s : Finset α} [Nonempty s] {p : Prop} : (∀ x ∈ s, p) ↔ p := + (nonempty_coe_sort.mp ‹_›).forall_const + theorem Nonempty.to_subtype {s : Finset α} : s.Nonempty → Nonempty s := nonempty_coe_sort.2 diff --git a/Mathlib/Data/Finset/Lattice/Fold.lean b/Mathlib/Data/Finset/Lattice/Fold.lean index b2f3b900f734bb..0f82aa39ee7c54 100644 --- a/Mathlib/Data/Finset/Lattice/Fold.lean +++ b/Mathlib/Data/Finset/Lattice/Fold.lean @@ -260,6 +260,10 @@ theorem sup_mem (s : Set α) (w₁ : ⊥ ∈ s) (w₂ : ∀ᵉ (x ∈ s) (y ∈ protected theorem sup_eq_bot_iff (f : β → α) (S : Finset β) : S.sup f = ⊥ ↔ ∀ s ∈ S, f s = ⊥ := by classical induction S using Finset.induction <;> simp [*] +@[to_additive (attr := simp)] +lemma sup_eq_one [One α] [IsBotOneClass α] : s.sup f = 1 ↔ ∀ i ∈ s, f i = 1 := by + simp [← bot_eq_one] + @[to_dual (attr := simp)] lemma sup_disjSum (s : Finset β) (t : Finset γ) (f : β ⊕ γ → α) : (s.disjSum t).sup f = (s.sup fun x ↦ f (.inl x)) ⊔ (t.sup fun x ↦ f (.inr x)) := @@ -663,14 +667,18 @@ end Sup' section Sup -variable [SemilatticeSup α] [OrderBot α] +variable [SemilatticeSup α] [OrderBot α] {s : Finset β} {f : β → α} @[to_dual] -theorem sup'_eq_sup {s : Finset β} (H : s.Nonempty) (f : β → α) : s.sup' H f = s.sup f := +theorem sup'_eq_sup (H : s.Nonempty) (f : β → α) : s.sup' H f = s.sup f := le_antisymm (sup'_le H f fun _ => le_sup) (Finset.sup_le fun _ => le_sup' f) +@[to_additive (attr := simp)] +lemma sup'_eq_one [One α] [IsBotOneClass α] (hs) : s.sup' hs f = 1 ↔ ∀ i ∈ s, f i = 1 := by + simp [sup'_eq_sup] + @[to_dual] -theorem coe_sup_of_nonempty {s : Finset β} (h : s.Nonempty) (f : β → α) : +theorem coe_sup_of_nonempty (h : s.Nonempty) (f : β → α) : (↑(s.sup f) : WithBot α) = s.sup ((↑) ∘ f) := by simp only [← sup'_eq_sup h, coe_sup' h] end Sup diff --git a/Mathlib/Data/Finset/MulAntidiagonal.lean b/Mathlib/Data/Finset/MulAntidiagonal.lean index 0390ccad26f580..f3da3e9b6a94e3 100644 --- a/Mathlib/Data/Finset/MulAntidiagonal.lean +++ b/Mathlib/Data/Finset/MulAntidiagonal.lean @@ -57,7 +57,7 @@ variable [CommMonoid α] [PartialOrder α] [IsOrderedCancelMonoid α] /-- `Finset.mulAntidiagonal hs ht a` is the set of all pairs of an element in `s` and an element in `t` that multiply to `a`, but its construction requires proofs that `s` and `t` are well-ordered. -/ -@[to_additive /-- `Finset.addAntidiagonal hs ht a` is the set of all pairs of an element in +@[to_additive /-- `Finset.antidiagonal hs ht a` is the set of all pairs of an element in `s` and an element in `t` that add to `a`, but its construction requires proofs that `s` and `t` are well-ordered. -/] noncomputable def mulAntidiagonal : Finset (α × α) := @@ -108,4 +108,15 @@ theorem mulAntidiagonal_min_mul_min {α} [CommMonoid α] [LinearOrder α] [IsOrd · rintro ⟨rfl, rfl⟩ exact ⟨hs.min_mem _, ht.min_mem _, rfl⟩ +@[deprecated (since := "2026-06-08")] alias addAntidiagonal := antidiagonal +@[deprecated (since := "2026-06-08")] alias mem_addAntidiagonal := mem_antidiagonal +@[deprecated (since := "2026-06-08")] alias addAntidiagonal_mono_left := antidiagonal_mono_left +@[deprecated (since := "2026-06-08")] alias addAntidiagonal_mono_right := antidiagonal_mono_right +@[deprecated (since := "2026-06-08")] alias swap_mem_addAntidiagonal := swap_mem_antidiagonal +@[deprecated (since := "2026-06-08")] +alias support_addAntidiagonal_subset_add := support_antidiagonal_subset_add +@[deprecated (since := "2026-06-08")] +alias isPWO_support_addAntidiagonal := isPWO_support_antidiagonal +@[deprecated (since := "2026-06-08")] alias addAntidiagonal_min_mul_min := antidiagonal_min_add_min + end Finset diff --git a/Mathlib/Data/Finset/NatAntidiagonal.lean b/Mathlib/Data/Finset/NatAntidiagonal.lean index a9bc329d17e3ee..9bfb92c051c2aa 100644 --- a/Mathlib/Data/Finset/NatAntidiagonal.lean +++ b/Mathlib/Data/Finset/NatAntidiagonal.lean @@ -30,6 +30,8 @@ open Function namespace Finset +open Finset.HasAntidiagonal + namespace Nat /-- The antidiagonal of a natural number `n` is diff --git a/Mathlib/Data/Finsupp/BigOperators.lean b/Mathlib/Data/Finsupp/BigOperators.lean index c05a5d5fee340e..686b4a2f52a53a 100644 --- a/Mathlib/Data/Finsupp/BigOperators.lean +++ b/Mathlib/Data/Finsupp/BigOperators.lean @@ -10,8 +10,8 @@ public import Mathlib.Algebra.Group.Finsupp public import Mathlib.Data.Finset.Pairwise /-! - # Sums of collections of Finsupp, and their support + This file provides results about the `Finsupp.support` of sums of collections of `Finsupp`, including sums of `List`, `Multiset`, and `Finset`. diff --git a/Mathlib/Data/Fintype/Card.lean b/Mathlib/Data/Fintype/Card.lean index fa521717a60e9c..3d37ede9458d79 100644 --- a/Mathlib/Data/Fintype/Card.lean +++ b/Mathlib/Data/Fintype/Card.lean @@ -255,6 +255,13 @@ theorem card_lt_of_injective_of_notMem (f : α → β) (h : Function.Injective f Finset.card_lt_univ_of_notMem (x := b) <| by rwa [← mem_coe, coe_map, coe_univ, Set.image_univ] +/-- Given an injective map `f : α → β` such that `β` has cardinality one more +than `α`, there exists a unique element of `β` not in the image of `f`. -/ +theorem existsUnique_notMem_image_of_injective_of_card_eq_add_one [DecidableEq β] + (f : α → β) (hf : f.Injective) (h : card β = card α + 1) : ∃! x, x ∉ univ.image f := by + simpa using existsUnique_notMem_image_of_injOn_of_card_eq_add_one + (s := .univ) (t := .univ) (Set.injOn_of_injective hf) (by simp) (by simpa) + theorem card_lt_of_injective_not_surjective (f : α → β) (h : Function.Injective f) (h' : ¬Function.Surjective f) : card α < card β := let ⟨_y, hy⟩ := not_forall.1 h' diff --git a/Mathlib/Data/FunLike/IsApply.lean b/Mathlib/Data/FunLike/IsApply.lean index 170abd0432cac1..e8848acbcec5a7 100644 --- a/Mathlib/Data/FunLike/IsApply.lean +++ b/Mathlib/Data/FunLike/IsApply.lean @@ -6,7 +6,9 @@ Authors: Moritz Doll module public import Mathlib.Algebra.Notation.Pi.Defs +public import Mathlib.Algebra.Group.Defs public import Mathlib.Data.FunLike.Basic +public import Mathlib.Logic.Function.Iterate /-! # Typeclasses for `FunLike` and algebraic operations In this file we provide typeclasses for the compatibility of algebraic structures and `FunLike` @@ -110,6 +112,14 @@ class IsMulApplyEqComp (F : Type*) (α : outParam Type*) [FunLike F α α] [Mul @[simp, grind =] alias mul_apply_eq_comp := IsMulApplyEqComp.mul_apply_eq_comp +@[simp, grind =] +lemma pow_apply_eq_iterate {F α : Type*} [FunLike F α α] [Monoid F] [IsOneApplyEqSelf F α] + [IsMulApplyEqComp F α] (f : F) (n : ℕ) (x : α) : + (f ^ n) x = f^[n] x := by + induction n with + | zero => simp + | succ n ih => simp [pow_succ', ih, ← Function.iterate_succ_apply'] + end Add section Sub @@ -245,6 +255,11 @@ theorem coe_one_eq_id_iff [One F'] [IsOneApplyEqSelf F' α] (f : F') : (f : α theorem coe_mul_eq_comp [Mul F'] [IsMulApplyEqComp F' α] (f g : F') : ↑(f * g) = f ∘ g := by ext; simp +@[norm_cast] +lemma coe_pow_eq_iterate [Monoid F'] [IsMulApplyEqComp F' α] [IsOneApplyEqSelf F' α] + (f : F') (n : ℕ) : ⇑(f ^ n) = f^[n] := + funext <| pow_apply_eq_iterate f n + @[norm_cast] theorem coe_natCast [NatCast F'] [One F'] [SMul Nat α] [SMul Nat F'] [IsSMulApply Nat F' α α] [IsNatCastApply F' α] [IsOneApplyEqSelf F' α] (n : Nat) : diff --git a/Mathlib/Data/LawfulXor.lean b/Mathlib/Data/LawfulXor.lean new file mode 100644 index 00000000000000..bb486390d9a029 --- /dev/null +++ b/Mathlib/Data/LawfulXor.lean @@ -0,0 +1,129 @@ +/- +Copyright (c) 2026 Eric Wieser. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Eric Wieser +-/ + +module +public import Mathlib.Logic.Function.Basic + +/-! +# The `LawfulXor` typeclass + +This file generalizes basic lemmas about the `^^^` operator across numeric types. +-/ + +@[expose] public section + +/-- A typeclass indicating that the xor operation, `^^^`, is lawful. -/ +class LawfulXor (α : Type*) [XorOp α] [Zero α] where + xor_assoc (a b c : α) : (a ^^^ b) ^^^ c = a ^^^ (b ^^^ c) + xor_self (a : α) : a ^^^ a = 0 + xor_zero (a : α) : a ^^^ 0 = a + xor_comm (a b : α) : a ^^^ b = b ^^^ a + +export LawfulXor (xor_assoc xor_self xor_zero xor_comm) + +variable {α : Type*} [XorOp α] [Zero α] [LawfulXor α] + +attribute [simp] xor_zero LawfulXor.xor_self + +@[simp] +theorem zero_xor (a : α) : 0 ^^^ a = a := by rw [LawfulXor.xor_comm, xor_zero] + +instance : Std.Commutative (α := α) XorOp.xor where comm := xor_comm +instance : Std.Associative (α := α) XorOp.xor where assoc := xor_assoc + +instance : Std.LawfulCommIdentity (α := α) XorOp.xor 0 where + left_id := zero_xor + right_id := xor_zero + +@[simp] +theorem xor_cancel_right (a b : α) : (a ^^^ b) ^^^ b = a := by + rw [xor_assoc, LawfulXor.xor_self, xor_zero] + +@[simp] +theorem xor_cancel_left (a b : α) : a ^^^ (a ^^^ b) = b := by + rw [← xor_assoc, LawfulXor.xor_self, zero_xor] + +instance : LawfulXor Nat where + xor_assoc := Nat.xor_assoc + xor_comm := Nat.xor_comm + xor_self := Nat.xor_self + xor_zero := Nat.xor_zero + +instance {w : Nat} : LawfulXor (BitVec w) where + xor_assoc := BitVec.xor_assoc + xor_comm := BitVec.xor_comm + xor_self _ := BitVec.xor_self + xor_zero _ := BitVec.xor_zero + +instance : LawfulXor UInt8 where + xor_assoc := UInt8.xor_assoc + xor_comm := UInt8.xor_comm + xor_self _ := UInt8.xor_self + xor_zero _ := UInt8.xor_zero + +instance : LawfulXor UInt16 where + xor_assoc := UInt16.xor_assoc + xor_comm := UInt16.xor_comm + xor_self _ := UInt16.xor_self + xor_zero _ := UInt16.xor_zero + +instance : LawfulXor UInt32 where + xor_assoc := UInt32.xor_assoc + xor_comm := UInt32.xor_comm + xor_self _ := UInt32.xor_self + xor_zero _ := UInt32.xor_zero + +instance : LawfulXor UInt64 where + xor_assoc := UInt64.xor_assoc + xor_comm := UInt64.xor_comm + xor_self _ := UInt64.xor_self + xor_zero _ := UInt64.xor_zero + +instance : LawfulXor USize where + xor_assoc := USize.xor_assoc + xor_comm := USize.xor_comm + xor_self _ := USize.xor_self + xor_zero _ := USize.xor_zero + +instance : LawfulXor Int8 where + xor_assoc := Int8.xor_assoc + xor_comm := Int8.xor_comm + xor_self _ := Int8.xor_self + xor_zero _ := Int8.xor_zero + +instance : LawfulXor Int16 where + xor_assoc := Int16.xor_assoc + xor_comm := Int16.xor_comm + xor_self _ := Int16.xor_self + xor_zero _ := Int16.xor_zero + +instance : LawfulXor Int32 where + xor_assoc := Int32.xor_assoc + xor_comm := Int32.xor_comm + xor_self _ := Int32.xor_self + xor_zero _ := Int32.xor_zero + +instance : LawfulXor Int64 where + xor_assoc := Int64.xor_assoc + xor_comm := Int64.xor_comm + xor_self _ := Int64.xor_self + xor_zero _ := Int64.xor_zero + +instance : LawfulXor ISize where + xor_assoc := ISize.xor_assoc + xor_comm := ISize.xor_comm + xor_self _ := ISize.xor_self + xor_zero _ := ISize.xor_zero + +lemma xor_right_involutive (a : α) : Function.Involutive (a ^^^ ·) := xor_cancel_left a + +lemma xor_left_involutive (a : α) : Function.Involutive (· ^^^ a) := (xor_cancel_right · a) + +lemma xor_eq_iff_left_eq (a b c : α) : + a ^^^ b = c ↔ a = c ^^^ b := xor_left_involutive _ |>.eq_iff + +lemma xor_eq_iff_right_eq (a b c : α) : + a ^^^ b = c ↔ b = a ^^^ c := xor_right_involutive _ |>.eq_iff diff --git a/Mathlib/Data/List/Chain.lean b/Mathlib/Data/List/Chain.lean index 0c4f488326a865..5e240bbd46b04e 100644 --- a/Mathlib/Data/List/Chain.lean +++ b/Mathlib/Data/List/Chain.lean @@ -8,8 +8,8 @@ module public import Mathlib.Data.List.Forall2 public import Mathlib.Data.List.Induction public import Mathlib.Data.List.Lex +public import Mathlib.Data.List.Pairwise public import Mathlib.Logic.Function.Iterate -public import Mathlib.Logic.Relation /-! # Relation chain @@ -25,13 +25,11 @@ public section assert_not_imported Mathlib.Algebra.Order.Group.Nat -universe u v - open Nat -namespace List +variable {α β : Type*} {R r : α → α → Prop} {l l₁ l₂ : List α} {a b : α} -variable {α : Type u} {β : Type v} {R r : α → α → Prop} {l l₁ l₂ : List α} {a b : α} +namespace List mk_iff_of_inductive_prop List.IsChain List.isChain_iff @@ -193,12 +191,17 @@ protected theorem IsChain.rel_cons [Trans R R R] (hl : (a :: l).IsChain R) (hb : theorem IsChain.tail {l : List α} (h : IsChain R l) : IsChain R l.tail := by grind +splitIndPred -theorem IsChain.rel_head {x y l} (h : IsChain R (x :: y :: l)) : R x y := - List.rel_of_isChain_cons_cons h +@[deprecated (since := "2026-06-25")] alias IsChain.rel_head := IsChain.rel theorem IsChain.rel_head? {x l} (h : IsChain R (x :: l)) ⦃y⦄ (hy : y ∈ head? l) : R x y := by rw [← cons_head?_tail hy] at h - exact h.rel_head + exact h.rel + +theorem IsChain.rel_getLast_dropLast {l : List α} (h : l.IsChain R) (hne : l.dropLast ≠ []) : + R (l.dropLast.getLast hne) (l.getLast <| by grind) := + match l with + | [_, _] => h.rel + | _ :: _ :: _ :: _ => h.tail.rel_getLast_dropLast <| by simp theorem IsChain.cons {x} : ∀ {l : List α}, IsChain R l → (∀ y ∈ l.head?, R x y) → IsChain R (x :: l) @@ -232,6 +235,12 @@ theorem IsChain.left_of_append (h : IsChain R (l₁ ++ l₂)) : IsChain R l₁ : theorem IsChain.right_of_append (h : IsChain R (l₁ ++ l₂)) : IsChain R l₂ := (isChain_append.1 h).2.1 +theorem IsChain.rel_getLast_head_of_append {l₁ l₂ : List α} (h : (l₁ ++ l₂).IsChain R) + (h₁ : l₁ ≠ []) (h₂ : l₂ ≠ []) : R (l₁.getLast h₁) (l₂.head h₂) := + match l₁, l₂ with + | [_], _ :: _ => h.rel + | _ :: _ :: _, _ :: _ => h.tail.rel_getLast_head_of_append (by simp) (by simp) + theorem IsChain.infix (h : IsChain R l) (h' : l₁ <:+: l) : IsChain R l₁ := by rcases h' with ⟨l₂, l₃, rfl⟩ exact h.left_of_append.right_of_append @@ -455,11 +464,29 @@ theorem isChain_cons_eq_iff_eq_replicate {a : α} {l : List α} : end List +theorem WellFoundedRelation.asymmetricₙ [WellFoundedRelation α] {l : List α} (hne : l ≠ []) + (h : l.IsChain WellFoundedRelation.rel) : + ¬WellFoundedRelation.rel (l.getLast hne) (l.head hne) := + match l with + | [x] => irrefl x + | _ :: _ :: _ => + fun hr ↦ asymmetricₙ (List.cons_ne_nil _ _) (h.dropLast.cons_cons hr) (h.rel_getLast_dropLast _) +termination_by l.head hne + +theorem WellFounded.asymmetricₙ (wf : WellFounded r) (hne : l ≠ []) (h : l.IsChain r) : + ¬r (l.getLast hne) (l.head hne) := + @WellFoundedRelation.asymmetricₙ α ⟨r, wf⟩ l hne h + +theorem WellFounded.listPairwise_reverse_compl (wf : WellFounded r) (h : l.IsChain r) : + l.reverse.Pairwise rᶜ := by + refine List.pairwise_iff_forall_infix.mpr fun l' hne hsub ↦ ?_ + have := wf.asymmetricₙ (by grind) <| h.infix <| l.reverse_reverse ▸ hsub.reverse + simpa /-! In this section, we consider the type of `r`-decreasing chains (`List.IsChain (flip r)`) equipped with lexicographic order `List.Lex r`. -/ -variable {α : Type*} (r : α → α → Prop) +variable (r) /-- The type of `r`-decreasing chains -/ abbrev List.chains := { l : List α // l.IsChain (flip r) } diff --git a/Mathlib/Data/List/InsertNth.lean b/Mathlib/Data/List/InsertNth.lean deleted file mode 100644 index 56dc8fbabb529d..00000000000000 --- a/Mathlib/Data/List/InsertNth.lean +++ /dev/null @@ -1,18 +0,0 @@ -/- -Copyright (c) 2024 Lean FRO. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Kim Morrison --/ -module - -public import Mathlib.Tactic.Common -public import Mathlib.Util.CompileInductive - -/-! -This is a stub file for importing `Mathlib/Data/List/InsertNth.lean`, -which has been renamed to `Mathlib/Data/List/InsertIdx.lean`. - -This file can be removed once the deprecation for `List.insertNth` is removed. --/ - -public section diff --git a/Mathlib/Data/List/Pairwise.lean b/Mathlib/Data/List/Pairwise.lean index 4ea247eb21099f..7860619d193500 100644 --- a/Mathlib/Data/List/Pairwise.lean +++ b/Mathlib/Data/List/Pairwise.lean @@ -40,6 +40,13 @@ mk_iff_of_inductive_prop List.Pairwise List.pairwise_iff /-! ### Pairwise -/ +theorem pairwise_iff_forall_infix {α : Type*} {l : List α} {R : α → α → Prop} : + l.Pairwise R ↔ + ∀ l', (h : 1 < l'.length) → l' <:+: l → R (l'.head <| by grind) (l'.getLast <| by grind) := by + refine l.pairwise_iff_getElem.trans ⟨fun h l' hne ⟨l₁, l₂, hl⟩ ↦ ?_, fun h i j hi hj hij ↦ ?_⟩ + · grind [getElem_append_left', getElem_append_right'] + · grind [h _ _ <| List.drop_suffix i _ |>.isInfix.trans <| l.take_prefix (j + 1) |>.isInfix] + theorem Pairwise.forall_of_forall [Std.Symm R] (H₁ : ∀ x ∈ l, R x x) (H₂ : l.Pairwise R) : ∀ ⦃x⦄, x ∈ l → ∀ ⦃y⦄, y ∈ l → R x y := H₂.forall_of_forall_of_flip H₁ <| by rwa [Std.Symm.flip_eq] diff --git a/Mathlib/Data/Matrix/Basic.lean b/Mathlib/Data/Matrix/Basic.lean index fdb89e65825736..6511fb8b1a3a9d 100644 --- a/Mathlib/Data/Matrix/Basic.lean +++ b/Mathlib/Data/Matrix/Basic.lean @@ -639,17 +639,16 @@ theorem mapMatrix_trans (f : α ≃+* β) (g : β ≃+* γ) : rfl open MulOpposite in -/-- -For any ring `R`, we have ring isomorphism `Matₙₓₙ(Rᵒᵖ) ≅ (Matₙₓₙ(R))ᵒᵖ` given by transpose. --/ +/-- For any ring `α`, we have ring isomorphism `Matₙₓₙ(αᵒᵖ) ≅ (Matₙₓₙ(α))ᵒᵖ` given by transpose. + +See also `Matrix.transposeRingEquiv` for a version that doesn't take the opposite of `α`, +given that its multiplication is commutative. -/ @[simps apply symm_apply] def mopMatrix {α} [Mul α] [AddCommMonoid α] : Matrix m m αᵐᵒᵖ ≃+* (Matrix m m α)ᵐᵒᵖ where toFun M := op (M.transpose.map unop) invFun M := M.unop.transpose.map op - left_inv _ := by aesop - right_inv _ := by aesop - map_mul' _ _ := unop_injective <| by ext; simp [transpose, mul_apply] - map_add' _ _ := by aesop + map_mul' _ _ := unop_injective <| by ext; simp [mul_apply] + map_add' _ _ := rfl end RingEquiv @@ -721,8 +720,10 @@ theorem mapMatrix_trans (f : α ≃ₐ[R] β) (g : β ≃ₐ[R] γ) : rfl /-- For any algebra `α` over a ring `R`, we have an `R`-algebra isomorphism -`Matₙₓₙ(αᵒᵖ) ≅ (Matₙₓₙ(R))ᵒᵖ` given by transpose. If `α` is commutative, -we can get rid of the `ᵒᵖ` in the left-hand side, see `Matrix.transposeAlgEquiv`. -/ +`Matₙₓₙ(αᵒᵖ) ≅ (Matₙₓₙ(R))ᵒᵖ` given by transpose. + +See also `Matrix.transposeAlgEquiv` for a version that doesn't take the opposite of `α`, +given that its multiplication is commutative. -/ @[simps!] def mopMatrix : Matrix m m αᵐᵒᵖ ≃ₐ[R] (Matrix m m α)ᵐᵒᵖ where __ := RingEquiv.mopMatrix commutes' _ := MulOpposite.unop_injective <| by @@ -894,7 +895,10 @@ theorem transposeLinearEquiv_symm [Semiring R] [AddCommMonoid α] [Module R α] variable {m n R α} variable (m α) -/-- `Matrix.transpose` as a `RingEquiv` to the opposite ring -/ +/-- `Matrix.transpose` as a `RingEquiv` to the opposite ring. + +See also `RingEquiv.mopMatrix` for a version that doesn't require `α` to have commutative +multiplication, by taking its opposite. -/ @[simps!] def transposeRingEquiv [AddCommMonoid α] [CommMagma α] [Fintype m] : Matrix m m α ≃+* (Matrix m m α)ᵐᵒᵖ where @@ -914,7 +918,10 @@ theorem transpose_list_prod [CommSemiring α] [Fintype m] [DecidableEq m] (l : L variable (R m α) -/-- `Matrix.transpose` as an `AlgEquiv` to the opposite ring -/ +/-- `Matrix.transpose` as an `AlgEquiv` to the opposite ring. + +See also `AlgEquiv.mopMatrix` for a version that doesn't require `α` to have commutative +multiplication, by taking its opposite. -/ @[simps!] def transposeAlgEquiv [CommSemiring R] [CommSemiring α] [Fintype m] [DecidableEq m] [Algebra R α] : Matrix m m α ≃ₐ[R] (Matrix m m α)ᵐᵒᵖ where diff --git a/Mathlib/Data/Matrix/Mul.lean b/Mathlib/Data/Matrix/Mul.lean index 6eaadb3b0e2fa1..19bcf3414eab9c 100644 --- a/Mathlib/Data/Matrix/Mul.lean +++ b/Mathlib/Data/Matrix/Mul.lean @@ -701,6 +701,12 @@ def mulVec [Fintype n] (M : Matrix m n α) (v : n → α) : m → α @[inherit_doc] scoped infixr:73 " *ᵥ " => Matrix.mulVec +lemma mulVec_apply [Fintype n] (M : Matrix m n α) (v : n → α) (i : m) : + (M *ᵥ v) i = M.row i ⬝ᵥ v := rfl + +lemma mulVec_apply_eq_sum [Fintype n] (M : Matrix m n α) (v : n → α) (i : m) : + (M *ᵥ v) i = ∑ j : n, M i j * v j := rfl + /-- `v ᵥ* M` (notation for `vecMul v M`) is the vector-matrix product of vector `v` and matrix `M`, where `v` is seen as a row vector. @@ -714,6 +720,12 @@ def vecMul [Fintype m] (v : m → α) (M : Matrix m n α) : n → α @[inherit_doc] scoped infixl:73 " ᵥ* " => Matrix.vecMul +lemma vecMul_apply [Fintype m] (v : m → α) (M : Matrix m n α) (i : n) : + (v ᵥ* M) i = v ⬝ᵥ M.col i := rfl + +lemma vecMul_apply_eq_sum [Fintype m] (v : m → α) (M : Matrix m n α) (i : n) : + (v ᵥ* M) i = ∑ j : m, v j * M j i := rfl + /-- Left multiplication by a matrix, as an `AddMonoidHom` from vectors to vectors. -/ @[simps] def mulVec.addMonoidHomLeft [Fintype n] (v : n → α) : Matrix m n α →+ m → α where @@ -1081,13 +1093,13 @@ theorem vecMul_transpose [Fintype n] (A : Matrix m n α) (x : n → α) : x ᵥ* apply dotProduct_comm /-- Bilinear form identity: `x ⬝ᵥ Aᵀ *ᵥ y = y ⬝ᵥ A *ᵥ x` for commutative semirings. -/ -theorem dotProduct_transpose_mulVec [Fintype m] (A : Matrix m m α) (x y : m → α) : - x ⬝ᵥ Aᵀ *ᵥ y = y ⬝ᵥ A *ᵥ x := by +theorem dotProduct_transpose_mulVec [Fintype m] [Fintype n] (A : Matrix m n α) (x : n → α) + (y : m → α) : x ⬝ᵥ Aᵀ *ᵥ y = y ⬝ᵥ A *ᵥ x := by rw [dotProduct_mulVec, dotProduct_comm, vecMul_transpose] /-- Bilinear form identity: `(x ᵥ* Aᵀ) ⬝ᵥ y = (y ᵥ* A) ⬝ᵥ x` for commutative semirings. -/ -theorem dotProduct_vecMul_transpose [Fintype m] (A : Matrix m m α) (x y : m → α) : - (x ᵥ* Aᵀ) ⬝ᵥ y = (y ᵥ* A) ⬝ᵥ x := by +theorem dotProduct_vecMul_transpose [Fintype m] [Fintype n] (A : Matrix m n α) (x : n → α) + (y : m → α) : (x ᵥ* Aᵀ) ⬝ᵥ y = (y ᵥ* A) ⬝ᵥ x := by simpa [dotProduct_mulVec] using dotProduct_transpose_mulVec (A := A) (x := x) (y := y) theorem mulVec_vecMul [Fintype n] [Fintype o] (A : Matrix m n α) (B : Matrix o n α) (x : o → α) : diff --git a/Mathlib/Data/NNReal/Defs.lean b/Mathlib/Data/NNReal/Defs.lean index d1a46797c16806..f67fe2b24c949d 100644 --- a/Mathlib/Data/NNReal/Defs.lean +++ b/Mathlib/Data/NNReal/Defs.lean @@ -165,6 +165,7 @@ theorem _root_.Real.le_coe_toNNReal (r : ℝ) : r ≤ Real.toNNReal r := le_max_left r 0 @[bound] theorem coe_nonneg (r : ℝ≥0) : (0 : ℝ) ≤ r := r.2 +@[simp] lemma not_toReal_neg {r : ℝ≥0} : ¬ r.toReal < 0 := r.coe_nonneg.not_gt @[simp, norm_cast] theorem coe_mk (a : ℝ) (ha) : toReal (.mk a ha) = a := rfl @@ -1006,8 +1007,8 @@ alias ⟨_, nnreal_coe_pos⟩ := coe_pos /-- Extension for the `positivity` tactic: cast from `ℝ≥0` to `ℝ`. -/ @[positivity NNReal.toReal _] -meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(NNReal.toReal $a) => assertInstancesCommute @@ -1019,8 +1020,8 @@ meta def evalNNRealtoReal : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for the `positivity` tactic: `Real.toNNReal` -/ @[positivity Real.toNNReal _] -meta def evalRealToNNReal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalRealToNNReal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ≥0), ~q(Real.toNNReal $a) => assertInstancesCommute @@ -1033,8 +1034,8 @@ alias ⟨_, nnabs_pos_of_pos⟩ := Real.nnabs_pos /-- Extension for the `positivity` tactic: `Real.nnabs` -/ @[positivity Real.nnabs _] -meta def evalRealNNAbs : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalRealNNAbs : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ≥0), ~q(Real.nnabs $a) => assertInstancesCommute diff --git a/Mathlib/Data/Nat/Choose/Lucas.lean b/Mathlib/Data/Nat/Choose/Lucas.lean index 5b7ca9df72bacb..b2192eea6d83e0 100644 --- a/Mathlib/Data/Nat/Choose/Lucas.lean +++ b/Mathlib/Data/Nat/Choose/Lucas.lean @@ -132,4 +132,97 @@ theorem choose_pow_mul_pow_mul_modEq_choose_nat : rw [← Int.natCast_modEq_iff] exact_mod_cast choose_pow_mul_pow_mul_modEq_choose +/-- For primes `p` and positive integer `n`, assume that for all `i ∈ Icc 1 (n - 1)`, +`choose n i` congruent to `0` module `p`, then `n = p ^ multiplicity p n`. +Also see `eq_pow_multiplicity_of_choose_modEq_zero_nat` for the version with `MOD`. -/ +theorem eq_pow_multiplicity_of_choose_modEq_zero (hn : 0 < n) + (h : ∀ i ∈ Icc 1 (n - 1), n.choose i ≡ 0 [ZMOD p]) : n = p ^ multiplicity p n := by + rename_i hp + by_contra! hn₀ + obtain ⟨m, hm⟩ := pow_multiplicity_dvd p n + specialize h (p ^ multiplicity p n) (by grind [le_of_dvd hn (pow_multiplicity_dvd p n)]) + nth_grw 1 [← mul_one (p ^ _), hm, choose_pow_mul_pow_mul_modEq_choose, choose_one_right] at h + suffices multiplicity p n + 1 ≤ multiplicity p n by lia + rw [← FiniteMultiplicity.pow_dvd_iff_le_multiplicity] + · nth_rw 2 [hm] + simpa [pow_add] using Nat.mul_dvd_mul_left _ (dvd_iff_mod_eq_zero.mpr (by exact_mod_cast h)) + · exact finiteMultiplicity_iff.mpr ⟨hp.out.ne_one, hn⟩ + +/-- For primes `p` and positive integer `n`, assume that for all `i ∈ Icc 1 (n - 1)`, +`choose n i` congruent to `0` module `p`, then `n = p ^ multiplicity p n`. +Also see `eq_pow_multiplicity_of_choose_modEq_zero` for the version with `ZMOD`. -/ +theorem eq_pow_multiplicity_of_choose_modEq_zero_nat (hn : 0 < n) + (h : ∀ i ∈ Icc 1 (n - 1), n.choose i ≡ 0 [MOD p]) : n = p ^ multiplicity p n := + eq_pow_multiplicity_of_choose_modEq_zero hn (by exact_mod_cast h) + +/-- For a prime power `n`, the minimal prime factor divides the greatest common divisor of +`choose n 1, ⋯, choose n (n - 1)`. -/ +theorem minFac_dvd_gcd_choose_of_isPrimePow (h : IsPrimePow n) : + n.minFac ∣ (Icc 1 (n - 1)).gcd n.choose := by + obtain ⟨k, _, _, hn₁⟩ := (isPrimePow_nat_iff_bounded_log_minFac _).mp h + exact dvd_gcd_iff.mpr fun i hi => by + nth_rw 2 [hn₁] + exact Prime.dvd_choose_pow (minFac_prime_iff.mpr h.ne_one) (by grind) (by grind) + +lemma minFac_sq_ndvd_gcd_choose_of_isPrimePow (h : IsPrimePow n) : + ¬ n.minFac ^ 2 ∣ (Icc 1 (n - 1)).gcd n.choose := by + obtain ⟨k, _, k_pos, hn₁⟩ := (isPrimePow_nat_iff_bounded_log_minFac _).mp h + have isPrime := minFac_prime_iff.mpr (IsPrimePow.ne_one h) + refine mt Finset.dvd_gcd_iff.mp ?_ + simp only [mem_Icc, not_forall] + have : n.minFac ^ (k - 1) ≤ n.minFac ^ k := Nat.pow_le_pow_right (minFac_pos n) (sub_le k 1) + refine ⟨n.minFac ^ (k - 1), ⟨one_le_pow _ _ (minFac_pos n), ?_⟩, ?_⟩ + · refine le_sub_one_of_lt ?_ + nth_rw 2 [hn₁] + exact Nat.pow_lt_pow_of_lt (Prime.one_lt isPrime) (sub_one_lt_of_lt k_pos) + · refine emultiplicity_lt_iff_not_dvd.mp ?_ + nth_rw 2 [hn₁] + rw [Nat.Prime.emultiplicity_choose_prime_pow isPrime this (pow_ne_zero _ + (Nat.Prime.ne_zero isPrime)), multiplicity_pow_self_of_prime (prime_iff.mp isPrime)] + norm_cast + grind + +lemma primeFactors_gcd_choose_of_isPrimePow (h : IsPrimePow n) : + ((Icc 1 (n - 1)).gcd n.choose).primeFactors = {n.minFac} := by + have ne_zero : (Icc 1 (n - 1)).gcd n.choose ≠ 0 := + gcd_ne_zero_iff.mpr ⟨1, by simp; grind [IsPrimePow.two_le h]⟩ + have isPrime := minFac_prime_iff.mpr (IsPrimePow.ne_one h) + refine eq_singleton_iff_unique_mem.mpr ⟨isPrime.mem_primeFactors + (minFac_dvd_gcd_choose_of_isPrimePow h) ne_zero, ?_⟩ + intro p hp + simp only [mem_primeFactors, ne_eq] at hp + obtain ⟨hp₁, hp₂, hp₃⟩ := hp + haveI : Fact (Nat.Prime p) := ⟨hp₁⟩ + simp_rw [Finset.dvd_gcd_iff, ← modEq_zero_iff_dvd] at hp₂ + have := eq_pow_multiplicity_of_choose_modEq_zero_nat h.pos hp₂ + have dvd_pow : n.minFac ∣ p ^ multiplicity p n := this ▸ minFac_dvd _ + exact (Nat.prime_dvd_prime_iff_eq isPrime hp₁).mp (isPrime.dvd_of_dvd_pow dvd_pow)|>.symm + +/-- For a prime power `n`, the greatest common divisor of `choose n 1, ⋯, choose n (n - 1)` +is actually the minimal prime factor of `n`. -/ +theorem gcd_choose_eq_minFac_of_isPrimePow (h : IsPrimePow n) : + (Icc 1 (n - 1)).gcd n.choose = n.minFac := by + have ne_zero : (Icc 1 (n - 1)).gcd n.choose ≠ 0 := + gcd_ne_zero_iff.mpr ⟨1, by simp; grind [IsPrimePow.two_le h]⟩ + have isPrime := minFac_prime_iff.mpr (IsPrimePow.ne_one h) + have : multiplicity n.minFac ((Icc 1 (n - 1)).gcd n.choose) = 1 := by + refine multiplicity_eq_of_dvd_of_not_dvd ?_ (minFac_sq_ndvd_gcd_choose_of_isPrimePow h) + simpa using minFac_dvd_gcd_choose_of_isPrimePow h + rw [Nat.prod_primeFactors_coe_pow_factorization ne_zero, primeFactors_gcd_choose_of_isPrimePow h] + simp [← Nat.multiplicity_eq_factorization isPrime ne_zero, this] + +/-- For a natural number `n` greater than `1`, assume that `n` is not a prime power, then +the greatest common divisor of `choose n 1, ⋯, choose n (n - 1)` is `1`. -/ +theorem gcd_choose_eq_one_of_not_isPrimePow (hn : 1 < n) (hpn : ¬ IsPrimePow n) : + (Icc 1 (n - 1)).gcd n.choose = 1 := by + contrapose! hpn + obtain ⟨q, hq, h⟩ := Nat.exists_prime_and_dvd hpn + simp_rw [Finset.dvd_gcd_iff, ← modEq_zero_iff_dvd] at h + haveI : Fact (Nat.Prime q) := ⟨hq⟩ + have := eq_pow_multiplicity_of_choose_modEq_zero_nat (zero_lt_of_lt hn) h + refine (isPrimePow_nat_iff n).mpr ⟨q, _, hq, Dvd.multiplicity_pos ?_, this.symm⟩ + specialize h 1 (by grind) + rw [choose_one_right, modEq_zero_iff_dvd] at h + exact h + end Choose diff --git a/Mathlib/Data/Nat/Choose/Mul.lean b/Mathlib/Data/Nat/Choose/Mul.lean deleted file mode 100644 index eb091c41ea5773..00000000000000 --- a/Mathlib/Data/Nat/Choose/Mul.lean +++ /dev/null @@ -1,10 +0,0 @@ -/- -Copyright (c) 2024 Antoine Chambert-Loir & María-Inés de Frutos—Fernández. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Antoine Chambert-Loir, María-Inés de Frutos—Fernández --/ -module -- shake: keep-all - -public import Mathlib.Tactic.Common - -deprecated_module (since := "2025-12-15") diff --git a/Mathlib/Data/Nat/DvdSequence.lean b/Mathlib/Data/Nat/DvdSequence.lean new file mode 100644 index 00000000000000..dcb4abb9a28910 --- /dev/null +++ b/Mathlib/Data/Nat/DvdSequence.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Thomas Browning. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Thomas Browning +-/ +module + +public import Mathlib.Algebra.Divisibility.Basic +public import Mathlib.Algebra.Group.Action.Pi + +/-! +# Divisibility sequences + +A sequence `f : ℕ → ℕ` is a *divisibility sequence* if it satisfies `f a ∣ f b` whenever `a ∣ b`. + +A sequence `f : ℕ → ℕ` is a *strong divisibility sequence* if `gcd (f a) (f b) = f (gcd a b)`. + +This file defines divisibility sequences and strong divisibility sequences, and provides some basic +API for these definitions. + +## Main definitions + +* `IsDvdSeq`: A function `f` is a divisibility sequence if `a ∣ b` implies `f a ∣ f b`. +* `Nat.IsStrongDvdSeq`: A function `f : ℕ → ℕ` is a strong divisibility sequence if `f` satisfies + `gcd (f a) (f b) = f (gcd a b)`. +-/ + +@[expose] public section + +variable {α β γ : Type*} + +-- this lemma regarding interaction between `smul` and `dvd` does not have a good home in mathlib +lemma smul_dvd_smul [Monoid α] [Monoid β] [SMul α β] [IsScalarTower α β β] + [IsScalarTower α α β] [SMulCommClass α β β] {a b : α} {c d : β} + (hab : a ∣ b) (hcd : c ∣ d) : (a • c) ∣ (b • d) := by + obtain ⟨⟨x, rfl⟩, ⟨y, rfl⟩⟩ := hab, hcd + exact ⟨x • y, mul_smul_mul_comm a x c y⟩ + +/-- A function `f : α → β` is a divisibility sequence if `a ∣ b` implies `f a ∣ f b`. -/ +def IsDvdSequence [Dvd α] [Dvd β] (f : α → β) : Prop := + ∀ a b, a ∣ b → f a ∣ f b + +namespace IsDvdSequence + +variable (α) in +protected theorem id [Dvd α] : IsDvdSequence (id : α → α) := + fun _ _ ↦ id + +variable (α) in +protected theorem const [Dvd α] [Monoid β] (b : β) : IsDvdSequence (fun _ : α ↦ b) := by + simp [IsDvdSequence] + +protected theorem smul' [Dvd α] [Monoid β] [Monoid γ] {f : α → β} {g : α → γ} [SMul β γ] + [IsScalarTower β γ γ] [IsScalarTower β β γ] [SMulCommClass β γ γ] + (hf : IsDvdSequence f) (hg : IsDvdSequence g) : IsDvdSequence (f • g) := + fun a b hab ↦ smul_dvd_smul (hf a b hab) (hg a b hab) + +protected theorem mul [Dvd α] [CommMonoid β] {f g : α → β} + (hf : IsDvdSequence f) (hg : IsDvdSequence g) : IsDvdSequence (f * g) := + .smul' hf hg + +protected theorem smul [Dvd α] [Monoid β] [Monoid γ] {f : α → γ} [SMul β γ] + [IsScalarTower β γ γ] [IsScalarTower β β γ] [SMulCommClass β γ γ] + (b : β) (hg : IsDvdSequence f) : IsDvdSequence (b • f) := + .smul' (.const α b) hg + +end IsDvdSequence + +namespace Nat + +/-- A function `f : ℕ → ℕ` is a strong divisibility sequence if `gcd (f a) (f b) = f (gcd a b)`. -/ +def IsStrongDvdSequence (f : ℕ → ℕ) : Prop := + ∀ a b, (f a).gcd (f b) = f (a.gcd b) + +namespace IsStrongDvdSequence + +theorem isDvdSequence {f : ℕ → ℕ} (hf : IsStrongDvdSequence f) : IsDvdSequence f := by + intro a b hab + simpa [gcd_eq_left hab, gcd_eq_left_iff_dvd] using hf a b + +protected theorem id : IsStrongDvdSequence (id : ℕ → ℕ) := + fun _ _ ↦ rfl + +protected theorem const (n : ℕ) : IsStrongDvdSequence (fun _ ↦ n) := by + simp [IsStrongDvdSequence] + +end IsStrongDvdSequence + +end Nat diff --git a/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean b/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean index 0d27087ca77713..7bb9f92d299545 100644 --- a/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean +++ b/Mathlib/Data/Nat/Factorial/DoubleFactorial.lean @@ -87,8 +87,8 @@ open Lean Meta Qq /-- Extension for `Nat.doubleFactorial`. -/ @[positivity Nat.doubleFactorial _] -meta def evalDoubleFactorial : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | pure .none +meta def evalDoubleFactorial : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.doubleFactorial $n) => assumeInstancesCommute diff --git a/Mathlib/Data/Nat/Factorization/Basic.lean b/Mathlib/Data/Nat/Factorization/Basic.lean index 7ba209ef2b7730..ef73fda01a634c 100644 --- a/Mathlib/Data/Nat/Factorization/Basic.lean +++ b/Mathlib/Data/Nat/Factorization/Basic.lean @@ -477,11 +477,16 @@ theorem prod_pow_prime_padicValNat (n : Nat) (hn : n ≠ 0) (m : Nat) (pr : n < · intro p hp simp [factorization_def n (prime_of_mem_primeFactors hp)] -lemma prod_pow_primeFactors_factorization (hn : n ≠ 0) : +theorem prod_primeFactors_pow_factorization (hn : n ≠ 0) : + n = ∏ p ∈ n.primeFactors, p ^ n.factorization p := + prod_factorization_pow_eq_self hn |>.symm.trans <| prod_factorization_eq_prod_primeFactors _ + +lemma prod_primeFactors_coe_pow_factorization (hn : n ≠ 0) : n = ∏ (p : n.primeFactors), (p : ℕ) ^ (n.factorization p) := by - nth_rw 1 [← prod_factorization_pow_eq_self hn] - rw [prod_factorization_eq_prod_primeFactors _] - exact prod_subtype n.primeFactors (fun _ ↦ Iff.rfl) fun a ↦ a ^ n.factorization a + simpa using prod_primeFactors_pow_factorization hn + +@[deprecated (since := "2026-06-24")] +alias prod_pow_primeFactors_factorization := prod_primeFactors_coe_pow_factorization lemma pairwise_coprime_pow_primeFactors_factorization : Pairwise (Function.onFun Nat.Coprime fun (p : n.primeFactors) ↦ p ^ n.factorization p) := by @@ -491,6 +496,26 @@ lemma pairwise_coprime_pow_primeFactors_factorization : · exact Nat.prime_of_mem_primeFactors p1.2 · exact Nat.prime_of_mem_primeFactors p2.2 +theorem dvd_prod_primeFactors_pow_self {n : ℕ} (hn : n ≠ 0) : + n ∣ (∏ p ∈ n.primeFactors, p) ^ n := by + nth_rw 1 [← Finset.prod_pow, prod_primeFactors_pow_factorization hn] + refine prod_dvd_prod_of_dvd _ _ fun i hi ↦ pow_dvd_pow i ?_ + grw [n.factorization_def <| prime_of_mem_primeFactors hi, padicValNat_le_self] + +theorem dvd_pow_self_iff {n k : ℕ} (hn : n ≠ 0) (hk : k ≠ 0) : + n ∣ k ^ n ↔ n.primeFactors ⊆ k.primeFactors := by + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · grw [← Nat.primeFactors_pow k hn, Nat.primeFactors_mono h <| pow_ne_zero n hk] + · grw [dvd_prod_primeFactors_pow_self hn, prod_dvd_prod_of_subset _ _ _ h, prod_primeFactors_dvd] + +theorem exists_dvd_pow_iff {n k : ℕ} (hn : n ≠ 0) (hk : k ≠ 0) : + (∃ m, n ∣ k ^ m) ↔ n.primeFactors ⊆ k.primeFactors := by + refine ⟨fun ⟨m, h⟩ ↦ ?_, fun h ↦ ⟨n, dvd_pow_self_iff hn hk |>.mpr h⟩⟩ + rcases eq_or_ne m 0 with (rfl | hm) + · simp_all + rw [← Nat.primeFactors_pow k hm] + exact Nat.primeFactors_mono h <| pow_ne_zero m hk + /-! ### Lemmas about factorizations of particular functions -/ /-- Exactly `n / p` naturals in `[1, n]` are multiples of `p`. diff --git a/Mathlib/Data/Nat/Fib/Basic.lean b/Mathlib/Data/Nat/Fib/Basic.lean index 06ccbf89cac39b..133e9d9b25fa94 100644 --- a/Mathlib/Data/Nat/Fib/Basic.lean +++ b/Mathlib/Data/Nat/Fib/Basic.lean @@ -8,6 +8,7 @@ module public import Mathlib.Data.Finset.NatAntidiagonal public import Mathlib.Data.Nat.GCD.Basic public import Mathlib.Data.Nat.BinaryRec +public import Mathlib.Data.Nat.DvdSequence public import Mathlib.Logic.Function.Iterate public import Mathlib.Tactic.Ring public import Mathlib.Tactic.Zify @@ -243,8 +244,13 @@ theorem fib_gcd (m n : ℕ) : fib (gcd m n) = gcd (fib m) (fib n) := by conv_rhs => rw [← mod_add_div' n m] rwa [gcd_fib_add_mul_self m (n % m) (n / m), gcd_comm (fib m) _] -theorem fib_dvd (m n : ℕ) (h : m ∣ n) : fib m ∣ fib n := by - rwa [← gcd_eq_left_iff_dvd, ← fib_gcd, gcd_eq_left_iff_dvd.mpr] +theorem isStrongDvdSequence_fib : IsStrongDvdSequence fib := + fun m n ↦ (fib_gcd m n).symm + +theorem isDvdSequence_fib : IsDvdSequence fib := + isStrongDvdSequence_fib.isDvdSequence + +alias fib_dvd := isDvdSequence_fib theorem fib_succ_eq_sum_choose : ∀ n : ℕ, fib (n + 1) = ∑ p ∈ Finset.antidiagonal n, choose p.1 p.2 := diff --git a/Mathlib/Data/Nat/Fib/Zeckendorf.lean b/Mathlib/Data/Nat/Fib/Zeckendorf.lean index baf33830ad15dc..5e417a6752f26e 100644 --- a/Mathlib/Data/Nat/Fib/Zeckendorf.lean +++ b/Mathlib/Data/Nat/Fib/Zeckendorf.lean @@ -37,7 +37,7 @@ fibonacci, zeckendorf, digit open List Nat -- TODO: The `local` attribute makes this not considered as an instance by linters -@[nolint defLemma docBlame] +@[nolint docBlame] local instance : IsTrans ℕ fun a b ↦ b + 2 ≤ a where trans _a _b _c hba hcb := hcb.trans <| le_self_add.trans hba diff --git a/Mathlib/Data/Nat/Find.lean b/Mathlib/Data/Nat/Find.lean index 250e74085224df..1a61e465b21bd9 100644 --- a/Mathlib/Data/Nat/Find.lean +++ b/Mathlib/Data/Nat/Find.lean @@ -29,6 +29,7 @@ private def lbp (m n : ℕ) : Prop := variable [DecidablePred p] (H : ∃ n, p n) +set_option linter.defProp false in set_option backward.privateInPublic true in private def wf_lbp : WellFounded (@lbp p) := ⟨let ⟨n, pn⟩ := H diff --git a/Mathlib/Data/Nat/MaxPowDiv.lean b/Mathlib/Data/Nat/MaxPowDiv.lean index ab67e60daed00f..caa09ce3da8c43 100644 --- a/Mathlib/Data/Nat/MaxPowDiv.lean +++ b/Mathlib/Data/Nat/MaxPowDiv.lean @@ -136,6 +136,19 @@ theorem pow_padicValNat_mul_divMaxPow (p n : ℕ) : p ^ padicValNat p n * divMax theorem _root_.pow_padicValNat_dvd {p n : ℕ} : p ^ padicValNat p n ∣ n := ⟨divMaxPow n p, by simp⟩ +theorem padicValNat_lt_self {p n : ℕ} (hn : n ≠ 0) : padicValNat p n < n := by + match p with + | 0 | 1 => simp [Nat.pos_of_ne_zero hn] + | p + 2 => + apply (p + 2 |>.pow_lt_pow_iff_right <| by lia).mp + apply Nat.lt_of_le_of_lt ?_ <| Nat.lt_pow_self <| by lia + exact le_of_dvd (Nat.pos_of_ne_zero hn) pow_padicValNat_dvd + +theorem padicValNat_le_self {p : ℕ} (n : ℕ) : padicValNat p n ≤ n := by + rcases eq_or_ne n 0 with rfl | hn + · simp + · exact Nat.le_of_lt <| padicValNat_lt_self hn + theorem not_dvd_divMaxPow {p n : ℕ} (hp : 1 < p) (hn : n ≠ 0) : ¬p ∣ divMaxPow n p := by simp [divMaxPow, maxPowDvdDiv, maxPowDvdDiv.go_spec, *] diff --git a/Mathlib/Data/Nat/Prime/Defs.lean b/Mathlib/Data/Nat/Prime/Defs.lean index 852308e4137880..5a4bb9cd22784d 100644 --- a/Mathlib/Data/Nat/Prime/Defs.lean +++ b/Mathlib/Data/Nat/Prime/Defs.lean @@ -162,6 +162,12 @@ much faster. instance decidablePrime (p : ℕ) : Decidable (Prime p) := decidable_of_iff' _ prime_def_lt' +/-! +### Specific small primes + +It is recommended not to add further lemmas to this list; instead, import +`Mathlib.Tactic.NormNum.Prime` in downstream files and use `norm_num` for primality proofs. +-/ theorem prime_two : Prime 2 := by decide theorem prime_three : Prime 3 := by decide diff --git a/Mathlib/Data/Nat/Sqrt.lean b/Mathlib/Data/Nat/Sqrt.lean index a2f2013abde8af..6e0e55b225761c 100644 --- a/Mathlib/Data/Nat/Sqrt.lean +++ b/Mathlib/Data/Nat/Sqrt.lean @@ -6,7 +6,6 @@ Authors: Floris van Doorn, Leonardo de Moura, Jeremy Avigad, Mario Carneiro module public import Mathlib.Data.Nat.Basic -public import Batteries.Data.Nat.Basic /-! # Properties of the natural number square root function. @@ -28,92 +27,8 @@ See [Wikipedia, *Methods of computing square roots*] (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)). -/ -private lemma iter_fp_bound (n k : ℕ) : - let iter_next (n guess : ℕ) := (guess + n / guess) / 2; - sqrt.iter n k ≤ iter_next n (sqrt.iter n k) := by - intro iter_next - unfold sqrt.iter - if h : (k + n / k) / 2 < k then - simpa [if_pos h] using iter_fp_bound _ _ - else - grind - -private lemma AM_GM : {a b : ℕ} → (4 * a * b ≤ (a + b) * (a + b)) - | 0, _ => by rw [Nat.mul_zero, Nat.zero_mul]; exact zero_le _ - | _, 0 => by rw [Nat.mul_zero]; exact zero_le _ - | a + 1, b + 1 => by - simpa only [Nat.mul_add, Nat.add_mul, show (4 : ℕ) = 1 + 1 + 1 + 1 from rfl, Nat.one_mul, - Nat.mul_one, Nat.add_assoc, Nat.add_left_comm, Nat.add_le_add_iff_left] - using Nat.add_le_add_right (@AM_GM a b) 4 - --- These two lemmas seem like they belong to `Batteries.Data.Nat.Basic`. - -lemma sqrt.iter_sq_le (n guess : ℕ) : sqrt.iter n guess * sqrt.iter n guess ≤ n := by - unfold sqrt.iter - let next := (guess + n / guess) / 2 - if h : next < guess then - simpa only [next, dif_pos h] using sqrt.iter_sq_le n next - else - apply Nat.mul_le_of_le_div - grind - -lemma sqrt.lt_iter_succ_sq (n guess : ℕ) (hn : n < (guess + 1) * (guess + 1)) : - n < (sqrt.iter n guess + 1) * (sqrt.iter n guess + 1) := by - unfold sqrt.iter - -- m was `next` - let m := (guess + n / guess) / 2 - dsimp - split_ifs with h - · suffices n < (m + 1) * (m + 1) by - simpa only [dif_pos h] using sqrt.lt_iter_succ_sq n m this - refine Nat.lt_of_mul_lt_mul_left ?_ (a := 4 * (guess * guess)) - apply Nat.lt_of_le_of_lt AM_GM - rw [show (4 : ℕ) = 2 * 2 from rfl] - rw [Nat.mul_mul_mul_comm 2, Nat.mul_mul_mul_comm (2 * guess)] - refine Nat.mul_self_lt_mul_self (?_ : _ < _ * ((_ / 2) + 1)) - rw [← add_div_right _ (by decide), Nat.mul_comm 2, Nat.mul_assoc, - show guess + n / guess + 2 = (guess + n / guess + 1) + 1 from rfl] - have aux_lemma {a : ℕ} : a ≤ 2 * ((a + 1) / 2) := by lia - refine lt_of_lt_of_le ?_ (Nat.mul_le_mul_left _ aux_lemma) - rw [Nat.add_assoc, Nat.mul_add] - exact Nat.add_lt_add_left (lt_mul_div_succ _ (lt_of_le_of_lt (Nat.zero_le m) h)) _ - · exact hn - -private def IsSqrt (n q : ℕ) : Prop := - q * q ≤ n ∧ n < (q + 1) * (q + 1) - -/- -Sketch of proof: -Up to rounding, in terms of the definition of `sqrt.iter`, - -* By AM-GM inequality, `next² ≥ n` giving one of the bounds. -* When we terminated, we have `guess ≥ next` from which we deduce the other bound `n ≥ next²`. - -To turn this into a lean proof we need to manipulate, use properties of natural number division etc. --/ -private lemma sqrt_isSqrt (n : ℕ) : IsSqrt n (sqrt n) := by - match n with - | 0 => simp [IsSqrt, sqrt] - | 1 => simp [IsSqrt, sqrt] - | n + 2 => - have h : ¬ (n + 2) ≤ 1 := by simp - simp only [IsSqrt, sqrt, h, ite_false] - refine ⟨sqrt.iter_sq_le _ _, sqrt.lt_iter_succ_sq _ _ ?_⟩ - simp only [Nat.mul_add, Nat.add_mul, Nat.one_mul, Nat.mul_one, ← Nat.add_assoc] - rw [Nat.lt_add_one_iff, Nat.add_assoc, ← Nat.mul_two] - refine le_trans (Nat.le_of_eq (div_add_mod' (n + 2) 2).symm) ?_ - rw [show (n + 2) / 2 * 2 + (n + 2) % 2 = n + 2 by grind] - simp only [shiftLeft_eq, Nat.one_mul] - refine Nat.le_of_lt (Nat.le_trans lt_log2_self (le_add_right_of_le ?_)) - rw [← Nat.pow_add] - grind - -lemma sqrt_le (n : ℕ) : sqrt n * sqrt n ≤ n := (sqrt_isSqrt n).left - lemma sqrt_le' (n : ℕ) : sqrt n ^ 2 ≤ n := by simpa [Nat.pow_two] using sqrt_le n -lemma lt_succ_sqrt (n : ℕ) : n < succ (sqrt n) * succ (sqrt n) := (sqrt_isSqrt n).right - lemma lt_succ_sqrt' (n : ℕ) : n < succ (sqrt n) ^ 2 := by simpa [Nat.pow_two] using lt_succ_sqrt n lemma sqrt_le_add (n : ℕ) : n ≤ sqrt n * sqrt n + sqrt n + sqrt n := by @@ -134,15 +49,8 @@ lemma sqrt_le_self (n : ℕ) : sqrt n ≤ n := le_trans (le_mul_self _) (sqrt_le @[gcongr] lemma sqrt_le_sqrt (h : m ≤ n) : sqrt m ≤ sqrt n := le_sqrt.2 (le_trans (sqrt_le _) h) -@[simp, grind =] lemma sqrt_zero : sqrt 0 = 0 := rfl - -@[simp, grind =] lemma sqrt_one : sqrt 1 = 1 := rfl - -lemma sqrt_eq_zero : sqrt n = 0 ↔ n = 0 := - ⟨fun h ↦ have := @sqrt_lt n 1; by grind, by grind⟩ - lemma eq_sqrt : a = sqrt n ↔ a * a ≤ n ∧ n < (a + 1) * (a + 1) := - ⟨fun e ↦ e.symm ▸ sqrt_isSqrt n, + ⟨fun e ↦ e.symm ▸ ⟨sqrt_le n, lt_succ_sqrt n⟩, fun ⟨h₁, h₂⟩ ↦ le_antisymm (le_sqrt.2 h₁) (le_of_lt_succ <| sqrt_lt.2 h₂)⟩ lemma eq_sqrt' : a = sqrt n ↔ a ^ 2 ≤ n ∧ n < (a + 1) ^ 2 := by @@ -178,8 +86,18 @@ lemma sqrt_succ_le_succ_sqrt (n : ℕ) : sqrt n.succ ≤ n.sqrt.succ := @[simp] lemma log2_two : (2 : ℕ).log2 = 1 := by simp [log2_def] +@[simp, grind =] lemma sqrt_zero : sqrt 0 = 0 := + eq_comm.1 (by simp [eq_sqrt]) + +@[simp, grind =] lemma sqrt_one : sqrt 1 = 1 := + eq_comm.1 (by simp [eq_sqrt]) + +lemma sqrt_eq_zero : sqrt n = 0 ↔ n = 0 := + ⟨fun h ↦ have := @sqrt_lt n 1; by grind, by grind⟩ + @[simp] -lemma sqrt_two : sqrt 2 = 1 := by simp [sqrt, sqrt.iter] +lemma sqrt_two : sqrt 2 = 1 := + eq_comm.1 (by simp [eq_sqrt]) lemma add_one_sqrt_le_of_ne_zero {n : ℕ} (hn : n ≠ 0) : (n + 1).sqrt ≤ n := le_induction (by simp) (fun n _ ih ↦ le_trans n.succ.sqrt_succ_le_succ_sqrt (succ_le_succ ih)) n diff --git a/Mathlib/Data/Nat/Squarefree.lean b/Mathlib/Data/Nat/Squarefree.lean index f4fb20713370c4..5fd20e08fc8cfd 100644 --- a/Mathlib/Data/Nat/Squarefree.lean +++ b/Mathlib/Data/Nat/Squarefree.lean @@ -13,6 +13,7 @@ public import Mathlib.RingTheory.UniqueFactorizationDomain.Nat /-! # Lemmas about squarefreeness of natural numbers + A number is squarefree when it is not divisible by any squares except the squares of units. ## Main Results @@ -373,6 +374,16 @@ lemma primeFactors_prod (hs : ∀ p ∈ s, p.Prime) : primeFactors (∏ p ∈ s, rintro ⟨hp, q, hq, hpq⟩ rwa [← ((hs _ hq).dvd_iff_eq hp.ne_one).1 hpq] +theorem primeFactors_prod_primeFactors (n : ℕ) : + (∏ p ∈ n.primeFactors, p).primeFactors = n.primeFactors := + primeFactors_prod fun _ hp ↦ n.mem_primeFactors.mp hp |>.left + +theorem prod_primeFactors_dvd_iff {n k : ℕ} (hk : k ≠ 0) : + (∏ p ∈ n.primeFactors, p) ∣ k ↔ n.primeFactors ⊆ k.primeFactors := by + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · grw [← Nat.primeFactors_mono h hk, primeFactors_prod_primeFactors] + · grw [← k.prod_primeFactors_dvd, Finset.prod_dvd_prod_of_subset _ _ _ h] + lemma primeFactors_div_gcd (hm : Squarefree m) (hn : n ≠ 0) : primeFactors (m / m.gcd n) = primeFactors m \ primeFactors n := by ext p diff --git a/Mathlib/Data/Nat/Totient.lean b/Mathlib/Data/Nat/Totient.lean index 82b080ac815907..c31f22ad920296 100644 --- a/Mathlib/Data/Nat/Totient.lean +++ b/Mathlib/Data/Nat/Totient.lean @@ -443,7 +443,8 @@ open Lean Meta Qq /-- Extension for `Nat.totient`. -/ @[positivity Nat.totient _] -meta def evalNatTotient : PositivityExt where eval {u α} z p e := do +meta def evalNatTotient : PositivityExt where eval {u α} z p e := + match p with | none => pure .none | some p => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.totient $n) => match ← core z p n with diff --git a/Mathlib/Data/PFunctor/Univariate/Basic.lean b/Mathlib/Data/PFunctor/Univariate/Basic.lean index 536ed9683b7150..a7c35efe92bc64 100644 --- a/Mathlib/Data/PFunctor/Univariate/Basic.lean +++ b/Mathlib/Data/PFunctor/Univariate/Basic.lean @@ -18,6 +18,9 @@ This file defines polynomial functors and the W-type construction as a polynomia universe u v uA uB uA₁ uB₁ uA₂ uB₂ v₁ v₂ v₃ +-- Note: `set_option linter.checkUnivs` should not apply here, +-- we really do want two separate universe levels +set_option linter.checkUnivs false in /-- 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 → α`. @@ -25,8 +28,7 @@ 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 `α`. -/ --- Note: `nolint checkUnivs` should not apply here, we really do want two separate universe levels -@[pp_with_univ, nolint checkUnivs] +@[pp_with_univ] structure PFunctor where /-- The head type -/ A : Type uA diff --git a/Mathlib/Data/PSigma/Order.lean b/Mathlib/Data/PSigma/Order.lean index d1f28e41cd256b..7a68fb944536a1 100644 --- a/Mathlib/Data/PSigma/Order.lean +++ b/Mathlib/Data/PSigma/Order.lean @@ -14,6 +14,7 @@ public import Mathlib.Order.Lex /-! # Lexicographic order on a sigma type + This file defines the lexicographic order on `Σₗ' i, α i`. `a` is less than `b` if its summand is strictly less than the summand of `b` or they are in the same summand and `a` is less than `b` there. diff --git a/Mathlib/Data/Rat/Cast/Order.lean b/Mathlib/Data/Rat/Cast/Order.lean index 935ce8e22c6bcf..8fecc133d64446 100644 --- a/Mathlib/Data/Rat/Cast/Order.lean +++ b/Mathlib/Data/Rat/Cast/Order.lean @@ -263,15 +263,21 @@ open Lean Meta Qq Function meta def evalRatCast : PositivityExt where eval {u α} _zα pα? e := do let ~q(@Rat.cast _ (_) ($a : ℚ)) := e | throwError "not Rat.cast" match ← core q(inferInstance) (some q(inferInstance)) a with - | .positive pa => - let some _ := pα? | pure .none - let _oα ← synthInstanceQ q(Field $α) - let _oα ← synthInstanceQ q(LinearOrder $α) - let _oα ← synthInstanceQ q(IsStrictOrderedRing $α) - assumeInstancesCommute - return .positive q((Rat.cast_pos (K := $α)).mpr $pa) - | .nonnegative pa => - let some _ := pα? | pure .none + | .positive pa => id <| + match pα? with + | none => do + let _oα ← synthInstanceQ q(DivisionRing $α) + let _cα ← synthInstanceQ q(CharZero $α) + assumeInstancesCommute + return .nonzero q((Rat.cast_ne_zero (α := $α)).mpr ($pa).ne') + | some _ => do + let _oα ← synthInstanceQ q(Field $α) + let _oα ← synthInstanceQ q(LinearOrder $α) + let _oα ← synthInstanceQ q(IsStrictOrderedRing $α) + assumeInstancesCommute + return .positive q((Rat.cast_pos (K := $α)).mpr $pa) + | .nonnegative pa => id <| + match pα? with | none => pure .none | some _ => do let _oα ← synthInstanceQ q(Field $α) let _oα ← synthInstanceQ q(LinearOrder $α) let _oα ← synthInstanceQ q(IsStrictOrderedRing $α) @@ -286,18 +292,17 @@ meta def evalRatCast : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for NNRat.cast. -/ @[positivity NNRat.cast _] -meta def evalNNRatCast : PositivityExt where eval {u α} _zα pα? e := do +meta def evalNNRatCast : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do let ~q(@NNRat.cast _ (_) ($a : ℚ≥0)) := e | throwError "not NNRat.cast" match ← core q(inferInstance) (some q(inferInstance)) a with | .positive pa => - let some _ := pα? | pure .none let _oα ← synthInstanceQ q(Semifield $α) let _oα ← synthInstanceQ q(LinearOrder $α) let _oα ← synthInstanceQ q(IsStrictOrderedRing $α) assumeInstancesCommute return .positive q((NNRat.cast_pos (K := $α)).mpr $pa) | _ => - let some _ := pα? | pure .none let _oα ← synthInstanceQ q(Semifield $α) let _oα ← synthInstanceQ q(LinearOrder $α) let _oα ← synthInstanceQ q(IsStrictOrderedRing $α) diff --git a/Mathlib/Data/Rat/Defs.lean b/Mathlib/Data/Rat/Defs.lean index 1b3f964e3a1f79..c252257fc8dd95 100644 --- a/Mathlib/Data/Rat/Defs.lean +++ b/Mathlib/Data/Rat/Defs.lean @@ -160,8 +160,10 @@ instance addCommGroup : AddCommGroup ℚ where rw [Rat.intCast_add, Rat.add_mul, Rat.intCast_one, Rat.one_mul] rfl zsmul_zero' := Rat.zero_mul - zsmul_succ' _ _ := by simp [Rat.add_mul] - zsmul_neg' _ _ := by rw [Int.negSucc_eq, Rat.intCast_neg, Rat.neg_mul]; rfl + zsmul_succ' _ _ := by simp_rw [HSMul.hSMul, SMul.smul]; simp [Rat.add_mul] + zsmul_neg' _ _ := by + simp_rw [HSMul.hSMul, SMul.smul] + rw [Int.negSucc_eq, Rat.intCast_neg, Rat.neg_mul]; rfl instance addGroup : AddGroup ℚ := by infer_instance diff --git a/Mathlib/Data/SProd.lean b/Mathlib/Data/SProd.lean index d18a913698b421..e69e918107300b 100644 --- a/Mathlib/Data/SProd.lean +++ b/Mathlib/Data/SProd.lean @@ -9,6 +9,7 @@ public import Mathlib.Tactic.FBinop /-! # Set Product Notation + This file provides notation for a product of sets, and other similar types. ## Main Definitions diff --git a/Mathlib/Data/Set/Basic.lean b/Mathlib/Data/Set/Basic.lean index ba08bcb023d83b..33b1e76ca63b6a 100644 --- a/Mathlib/Data/Set/Basic.lean +++ b/Mathlib/Data/Set/Basic.lean @@ -142,7 +142,6 @@ instance (s : Set α) : CoeTC s α := ⟨fun x => x.1⟩ theorem Set.coe_eq_subtype (s : Set α) : ↥s = { x // x ∈ s } := rfl -@[simp] theorem Set.coe_setOf (p : α → Prop) : ↥{ x | p x } = { x // p x } := rfl @@ -506,6 +505,14 @@ theorem subset_eq_empty {s t : Set α} (h : t ⊆ s) (e : s = ∅) : t = ∅ := theorem forall_mem_empty {p : α → Prop} : (∀ x ∈ (∅ : Set α), p x) ↔ True := iff_true_intro fun _ => False.elim +theorem Nonempty.forall_const (h : s.Nonempty) {p : Prop} : (∀ x ∈ s, p) ↔ p := + let ⟨x, hx⟩ := h + ⟨fun h ↦ h x hx, fun h _ _ ↦ h⟩ + +@[simp] +theorem forall_mem_const {p : Prop} [Nonempty s] : (∀ x ∈ s, p) ↔ p := + (nonempty_coe_sort.mp ‹_›).forall_const + instance (α : Type u) : IsEmpty.{u + 1} (↥(∅ : Set α)) := ⟨fun x => x.2⟩ diff --git a/Mathlib/Data/Set/Enumerate.lean b/Mathlib/Data/Set/Enumerate.lean index 8784e9c8ee2bc8..ba8b7a5df130d1 100644 --- a/Mathlib/Data/Set/Enumerate.lean +++ b/Mathlib/Data/Set/Enumerate.lean @@ -11,6 +11,7 @@ public import Mathlib.Data.Set.Insert /-! # Set enumeration + This file allows enumeration of sets given a choice function. The definition does not assume `sel` actually is a choice function, i.e. `sel s ∈ s` and `sel s = none ↔ s = ∅`. These assumptions are added to the lemmas needing them. diff --git a/Mathlib/Data/Set/Finite/Lattice.lean b/Mathlib/Data/Set/Finite/Lattice.lean index b748f7e9056729..5722f01641983a 100644 --- a/Mathlib/Data/Set/Finite/Lattice.lean +++ b/Mathlib/Data/Set/Finite/Lattice.lean @@ -269,7 +269,7 @@ variable {s t : Set α} theorem infinite_iUnion {ι : Type*} [Infinite ι] {s : ι → Set α} (hs : Function.Injective s) : (⋃ i, s i).Infinite := fun hfin ↦ @not_injective_infinite_finite ι _ _ hfin.finite_subsets.to_subtype - (fun i ↦ ⟨s i, subset_iUnion _ _⟩) fun i j h_eq ↦ hs (by simpa using h_eq) + (fun i ↦ ⟨s i, subset_iUnion _ _⟩) fun _ _ h_eq ↦ hs (Subtype.ext_iff.1 h_eq) theorem Infinite.biUnion {ι : Type*} {s : ι → Set α} {a : Set ι} (ha : a.Infinite) (hs : a.InjOn s) : (⋃ i ∈ a, s i).Infinite := by diff --git a/Mathlib/Data/Set/FiniteExhaustion.lean b/Mathlib/Data/Set/FiniteExhaustion.lean index 1d9bd2db3e8194..10b3a9449870e0 100644 --- a/Mathlib/Data/Set/FiniteExhaustion.lean +++ b/Mathlib/Data/Set/FiniteExhaustion.lean @@ -80,6 +80,7 @@ lemma _root_.Set.nonempty_finiteExhaustion_iff {s : Set α} : rw [← K.iUnion_eq] exact countable_iUnion <| fun i ↦ (K.finite i).countable +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias Set.nonempty_finiteExhaustion_iff := Set.nonempty_finiteExhaustion_iff diff --git a/Mathlib/Data/Set/UnionLift.lean b/Mathlib/Data/Set/UnionLift.lean index 297bbe7e714ed3..85d18856b04edf 100644 --- a/Mathlib/Data/Set/UnionLift.lean +++ b/Mathlib/Data/Set/UnionLift.lean @@ -10,6 +10,7 @@ public import Mathlib.Order.Directed /-! # Union lift + This file defines `Set.iUnionLift` to glue together functions defined on each of a collection of sets to make a function on the Union of those sets. diff --git a/Mathlib/Data/Setoid/Basic.lean b/Mathlib/Data/Setoid/Basic.lean index 0547b8285f1049..993671add13f70 100644 --- a/Mathlib/Data/Setoid/Basic.lean +++ b/Mathlib/Data/Setoid/Basic.lean @@ -38,7 +38,7 @@ attribute [refl, simp] Setoid.refl attribute [symm] Setoid.symm attribute [trans] Setoid.trans -variable {α : Type*} {β : Type*} +variable {α β γ : Type*} namespace Setoid @@ -55,6 +55,8 @@ instance : LE (Setoid α) := theorem le_def {r s : Setoid α} : r ≤ s ↔ ∀ {x y}, r x y → s x y := Iff.rfl +theorem le_iff_rel_le {r₁ r₂ : Setoid α} : r₁ ≤ r₂ ↔ ⇑r₁ ≤ ⇑r₂ := Iff.rfl + @[refl] theorem refl' (r : Setoid α) (x) : r x x := r.iseqv.refl x @@ -67,6 +69,9 @@ theorem trans' (r : Setoid α) : ∀ {x y z}, r x y → r y z → r x z := r.ise theorem comm' (s : Setoid α) {x y} : s x y ↔ s y x := ⟨s.symm', s.symm'⟩ +theorem comm [Setoid α] {x y : α} : x ≈ y ↔ y ≈ x := + ⟨Setoid.symm, Setoid.symm⟩ + open scoped Function -- required for scoped `on` notation /-- The kernel of a function is an equivalence relation. -/ @@ -189,11 +194,11 @@ instance completeLattice : CompleteLattice (Setoid α) := bot := ⟨(· = ·), ⟨fun _ => rfl, fun h => h.symm, fun h1 h2 => h1.trans h2⟩⟩ bot_le := fun r x _ h => h ▸ r.2.1 x } -@[simp] +@[simp, grind =] theorem top_def : ⇑(⊤ : Setoid α) = ⊤ := rfl -@[simp] +@[simp, grind =] theorem bot_def : ⇑(⊥ : Setoid α) = (· = ·) := rfl @@ -207,6 +212,9 @@ theorem eq_top_iff {s : Setoid α} : s = (⊤ : Setoid α) ↔ ∀ x y : α, s x rw [_root_.eq_top_iff, Setoid.le_def, Setoid.top_def] simp only [Pi.top_apply, Prop.top_eq_true, forall_true_left] +@[simp] +theorem ker_eq_bot_iff {f : α → β} : ker f = ⊥ ↔ f.Injective := le_bot_iff.symm + lemma sInf_equiv {S : Set (Setoid α)} {x y : α} : letI := sInf S x ≈ y ↔ ∀ s ∈ S, s x y := Iff.rfl @@ -401,6 +409,21 @@ related to the elements of `f⁻¹(y)` by `r`.' -/ def map (r : Setoid α) (f : α → β) : Setoid β := Relation.EqvGen.setoid (Relation.Map r f f) +theorem coe_map_of_ker_le (r : Setoid α) (f : α → β) (hf : ker f ≤ r) : + ⇑(map r f) = Relation.Map r f f ⊔ (· = ·) := by + refine le_antisymm ?_ (sup_le Relation.EqvGen.rel (by rintro _ _ rfl; exact .refl _)) + rintro _ _ hxy + induction hxy with + | rel _ _ hab => exact .inl hab + | refl _ => exact .inr rfl + | symm _ _ _ ih => exact ih.imp (Std.Symm.symm _ _) (Std.Symm.symm _ _) + | trans _ _ _ _ _ ih1 ih2 => + rcases ih1 with ih1 | rfl + · rcases ih2 with ih2 | rfl + · exact .inl <| r.iseqv.isTrans.map hf |>.trans _ _ _ ih1 ih2 + · exact .inl ih1 + · exact ih2 + /-- Given a surjective function f whose kernel is contained in an equivalence relation r, the equivalence relation on f's codomain defined by x ≈ y ↔ the elements of f⁻¹(x) are related to the elements of f⁻¹(y) by r. -/ @@ -420,6 +443,9 @@ See note [reducible non-instances]. -/ abbrev comap (f : α → β) (r : Setoid β) : Setoid α := ⟨r on f, r.iseqv.comap _⟩ +theorem comap_rel_eq (f : α → β) (r : Setoid β) : ⇑(comap f r) = (⇑r on f) := + rfl + theorem comap_rel (f : α → β) (r : Setoid β) (x y : α) : comap f r x y ↔ r (f x) (f y) := Iff.rfl @@ -428,6 +454,37 @@ induced on `α` by `f` equals the kernel of `r`'s quotient map composed with `f` theorem comap_eq {f : α → β} {r : Setoid β} : comap f r = ker (@Quotient.mk'' _ r ∘ f) := ext fun x y => show _ ↔ ⟦_⟧ = ⟦_⟧ by rw [Quotient.eq]; rfl +@[simp] +theorem comap_id (c : Setoid α) : c.comap id = c := rfl + +@[simp] +theorem comap_comp (c : Setoid γ) (g : β → γ) (f : α → β) : c.comap (g ∘ f) = (c.comap g).comap f := + rfl + +theorem comap_injective (f : α → β) (hf : Function.Surjective f) : + Function.Injective (comap f) := + fun _ _ h => ext <| hf.forall₂.2 <| Setoid.ext_iff.1 h + +theorem le_comap_map {r : Setoid α} {f : α → β} : r ≤ comap f (r.map f) := + fun _ _ h => Relation.EqvGen.rel _ _ ⟨_, _, h, rfl, rfl⟩ + +theorem comap_map_of_ker_le (f : α → β) (r : Setoid α) (hf : ker f ≤ r) : + comap f (r.map f) = r := by + apply le_antisymm _ le_comap_map + rw [le_iff_rel_le, comap_rel_eq, coe_map_of_ker_le _ _ hf] + rintro x y (⟨a, b, h, ha, hb⟩ | h) + · replace ha := hf ha + replace hb := hf hb + exact trans (symm ha) (trans h hb) + · exact hf h + +theorem comap_map_eq (f : α → β) (r : Setoid α) (hf : f.Injective) : comap f (r.map f) = r := + comap_map_of_ker_le f r <| ker_eq_bot_iff.2 hf ▸ bot_le + +theorem comap_surjective (f : α → β) (hf : Function.Injective f) : + Function.Surjective (Setoid.comap f) := + fun r => ⟨_, comap_map_eq f r hf⟩ + /-- The second isomorphism theorem for sets. -/ noncomputable def comapQuotientEquiv (f : α → β) (r : Setoid β) : Quotient (comap f r) ≃ Set.range (@Quotient.mk'' _ r ∘ f) := diff --git a/Mathlib/Data/Subtype.lean b/Mathlib/Data/Subtype.lean index 84c50fde8f3a6a..abce50412ff816 100644 --- a/Mathlib/Data/Subtype.lean +++ b/Mathlib/Data/Subtype.lean @@ -135,6 +135,10 @@ def coind {α β} (f : α → β) {p : β → Prop} (h : ∀ a, p (f a)) : α theorem coind_injective {α β} {f : α → β} {p : β → Prop} (h : ∀ a, p (f a)) (hf : Injective f) : Injective (coind f h) := fun x y hxy ↦ hf <| by apply congr_arg Subtype.val hxy +@[simp] theorem coind_injective_iff {α β} {f : α → β} {p : β → Prop} (h : ∀ a, p (f a)) : + Injective (coind f h) ↔ Injective f := + ⟨Subtype.coe_injective.comp, coind_injective h⟩ + /-- Restriction of a function to a function on subtypes. -/ @[simps] def map {p : α → Prop} {q : β → Prop} (f : α → β) (h : ∀ a, p a → q (f a)) : diff --git a/Mathlib/Data/Tree/Basic.lean b/Mathlib/Data/Tree/Basic.lean index 993a21a4c1953e..2a510b039f784b 100644 --- a/Mathlib/Data/Tree/Basic.lean +++ b/Mathlib/Data/Tree/Basic.lean @@ -6,6 +6,7 @@ Authors: Mario Carneiro, Wojciech Nawrocki module public import Mathlib.Data.Nat.Notation +public import Mathlib.Tactic.CrossRefAttribute public import Mathlib.Util.CompileInductive import Batteries.Tactic.Alias diff --git a/Mathlib/Data/ZMod/Defs.lean b/Mathlib/Data/ZMod/Defs.lean index e1b9832a88d149..e6b35cc6f970f0 100644 --- a/Mathlib/Data/ZMod/Defs.lean +++ b/Mathlib/Data/ZMod/Defs.lean @@ -65,17 +65,22 @@ open scoped Fin.IntCast Fin.NatCast lia /-- Multiplicative commutative semigroup structure on `Fin n`. -/ -instance instCommSemigroup (n : ℕ) : CommSemigroup (Fin n) := - { (inferInstance : Mul (Fin n)) with - mul_assoc := fun ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩ => - Fin.eq_of_val_eq <| - calc - a * b % n * c ≡ a * b * c [MOD n] := (Nat.mod_modEq _ _).mul_right _ - _ ≡ a * (b * c) [MOD n] := by rw [mul_assoc] - _ ≡ a * (b * c % n) [MOD n] := (Nat.mod_modEq _ _).symm.mul_left _ - mul_comm := Fin.mul_comm } - -set_option backward.privateInPublic true in +instance instCommSemigroup (n : ℕ) : CommSemigroup (Fin n) where + mul_assoc := fun ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩ => + Fin.eq_of_val_eq <| + calc + a * b % n * c ≡ a * b * c [MOD n] := (Nat.mod_modEq _ _).mul_right _ + _ ≡ a * (b * c) [MOD n] := by rw [mul_assoc] + _ ≡ a * (b * c % n) [MOD n] := (Nat.mod_modEq _ _).symm.mul_left _ + mul_comm := Fin.mul_comm + +-- Shortcut instances to replace the power operation on `Fin` with a more efficient one +instance (n : ℕ) [NeZero n] : HPow (Fin n) ℕ (Fin n) where + hPow a m := npowRecAuto m a + +instance (n : ℕ) [NeZero n] : Pow (Fin n) ℕ where + pow a m := npowRecAuto m a + private theorem left_distrib_aux (n : ℕ) : ∀ a b c : Fin n, a * (b + c) = a * b + a * c := fun ⟨a, _⟩ ⟨b, _⟩ ⟨c, _⟩ => Fin.eq_of_val_eq <| @@ -84,19 +89,13 @@ private theorem left_distrib_aux (n : ℕ) : ∀ a b c : Fin n, a * (b + c) = a _ ≡ a * b + a * c [MOD n] := by rw [mul_add] _ ≡ a * b % n + a * c % n [MOD n] := (Nat.mod_modEq _ _).symm.add (Nat.mod_modEq _ _).symm -set_option backward.privateInPublic true in -set_option backward.privateInPublic.warn false in /-- Distributive structure on `Fin n`. -/ -instance instDistrib (n : ℕ) : Distrib (Fin n) := - { Fin.addCommSemigroup n, Fin.instCommSemigroup n with - left_distrib := left_distrib_aux n - right_distrib := fun a b c => by - rw [mul_comm, left_distrib_aux, mul_comm _ b, mul_comm] } +instance instDistrib (n : ℕ) : Distrib (Fin n) where + left_distrib := private left_distrib_aux n + right_distrib := fun a b c => by + rw [mul_comm, left_distrib_aux, mul_comm _ b, mul_comm] instance instNonUnitalCommRing (n : ℕ) [NeZero n] : NonUnitalCommRing (Fin n) where - __ := Fin.addCommGroup n - __ := Fin.instCommSemigroup n - __ := Fin.instDistrib n zero_mul := Fin.zero_mul mul_zero := Fin.mul_zero @@ -106,7 +105,6 @@ instance instCommMonoid (n : ℕ) [NeZero n] : CommMonoid (Fin n) where /-- Note this is more general than `Fin.instCommRing` as it applies (vacuously) to `Fin 0` too. -/ instance instHasDistribNeg (n : ℕ) : HasDistribNeg (Fin n) where - toInvolutiveNeg := Fin.instInvolutiveNeg n mul_neg := Nat.casesOn n finZeroElim fun _i => mul_neg neg_mul := Nat.casesOn n finZeroElim fun _i => neg_mul @@ -126,11 +124,6 @@ silently introducing wraparound arithmetic. -/ @[instance_reducible] def instCommRing (n : ℕ) [NeZero n] : CommRing (Fin n) where - __ := Fin.instAddMonoidWithOne n - __ := Fin.addCommGroup n - __ := Fin.instCommSemigroup n - __ := Fin.instNonUnitalCommRing n - __ := Fin.instCommMonoid n intCast n := Fin.intCast n namespace CommRing diff --git a/Mathlib/Data/ZMod/IntUnitsPower.lean b/Mathlib/Data/ZMod/IntUnitsPower.lean index 687842a20a1f6a..e341f184756521 100644 --- a/Mathlib/Data/ZMod/IntUnitsPower.lean +++ b/Mathlib/Data/ZMod/IntUnitsPower.lean @@ -65,8 +65,8 @@ instance Int.instUnitsPow : Pow ℤˣ R where -- The above instances form no typeclass diamonds with the standard power operators -- but we will need `reducible_and_instances` which currently fails https://github.com/leanprover-community/mathlib4/issues/10906 -example : Int.instUnitsPow = Monoid.toPow := rfl -example : Int.instUnitsPow = DivInvMonoid.toZPow := rfl +example : Int.instUnitsPow = NPow.toPow := rfl +example : Int.instUnitsPow = ZPow.toPow := rfl @[simp] lemma ofMul_uzpow (u : ℤˣ) (r : R) : Additive.ofMul (u ^ r) = r • Additive.ofMul u := rfl diff --git a/Mathlib/Data/ZMod/QuotientRing.lean b/Mathlib/Data/ZMod/QuotientRing.lean index a2902af0327b75..8ae25270c721aa 100644 --- a/Mathlib/Data/ZMod/QuotientRing.lean +++ b/Mathlib/Data/ZMod/QuotientRing.lean @@ -84,10 +84,18 @@ def ZMod.prodEquivPi {ι : Type*} [Fintype ι] (a : ι → ℕ) quotientInfRingEquivPiQuotient _ this |>.trans <| RingEquiv.piCongrRight fun i ↦ Int.quotientSpanNatEquivZMod (a i) +open Finset Function in +@[simp] +theorem ZMod.prodEquivPi_apply {ι : Type*} [Fintype ι] (a : ι → ℕ) + (coprime : Pairwise (Nat.Coprime on a)) (b : ZMod (∏ i, a i)) (i : ι) : + prodEquivPi a coprime b i = castHom (dvd_prod_of_mem a (mem_univ i)) _ b := + RingHom.congr_fun (Subsingleton.elim ((Pi.evalRingHom (fun _ ↦ ZMod _) i).comp + (prodEquivPi a coprime).toRingHom) _) b + /-- The **Chinese remainder theorem**, version for `ZMod n`. -/ def ZMod.equivPi (hn : n ≠ 0) : ZMod n ≃+* Π (p : n.primeFactors), ZMod (p ^ (n.factorization p)) := - (ringEquivCongr <| Nat.prod_pow_primeFactors_factorization hn).trans + (ringEquivCongr <| Nat.prod_primeFactors_coe_pow_factorization hn).trans <| prodEquivPi (fun (p : n.primeFactors) ↦ (p : ℕ) ^ (n.factorization p)) n.pairwise_coprime_pow_primeFactors_factorization diff --git a/Mathlib/Dynamics/Flow.lean b/Mathlib/Dynamics/Flow.lean index bad8543dece3ee..87698e7d8c728b 100644 --- a/Mathlib/Dynamics/Flow.lean +++ b/Mathlib/Dynamics/Flow.lean @@ -34,13 +34,13 @@ flow onto an invariant subset, and the time-reversal of a flow by a group. open Set Function Filter +variable {τ α : Type*} + /-! ### Invariant sets -/ section Invariant -variable {τ : Type*} {α : Type*} - /-- A set `s ⊆ α` is invariant under `ϕ : τ → α → α` if `ϕ t s ⊆ s` for all `t` in `τ`. -/ def IsInvariant (ϕ : τ → α → α) (s : Set α) : Prop := ∀ t, MapsTo (ϕ t) s s @@ -76,21 +76,23 @@ end Invariant ### Flows -/ +variable (τ α) in /-- A flow on a topological space `α` by an additive topological monoid `τ` is a continuous monoid action of `τ` on `α`. -/ -structure Flow (τ : Type*) [TopologicalSpace τ] [AddMonoid τ] [ContinuousAdd τ] (α : Type*) - [TopologicalSpace α] where +structure Flow [TopologicalSpace τ] [TopologicalSpace α] [AddZero τ] where /-- The map `τ → α → α` underlying a flow of `τ` on `α`. -/ toFun : τ → α → α cont' : Continuous (uncurry toFun) map_add' : ∀ t₁ t₂ x, toFun (t₁ + t₂) x = toFun t₁ (toFun t₂ x) map_zero' : ∀ x, toFun 0 x = x - namespace Flow -variable {τ : Type*} [AddMonoid τ] [TopologicalSpace τ] [ContinuousAdd τ] - {α : Type*} [TopologicalSpace α] (ϕ : Flow τ α) +variable [TopologicalSpace τ] [TopologicalSpace α] + +section AddZero + +variable [AddZero τ] (ϕ : Flow τ α) instance : CoeFun (Flow τ α) fun _ => τ → α → α := ⟨Flow.toFun⟩ @@ -122,6 +124,10 @@ protected theorem continuous {β : Type*} [TopologicalSpace β] {t : β → τ} alias _root_.Continuous.flow := Flow.continuous +@[continuity, fun_prop] +theorem continuous_toFun (t : τ) : Continuous (ϕ.toFun t) := by + fun_prop + theorem map_add (t₁ t₂ : τ) (x : α) : ϕ (t₁ + t₂) x = ϕ t₁ (ϕ t₂ x) := ϕ.map_add' _ _ _ @[simp] @@ -132,13 +138,17 @@ theorem map_zero_apply (x : α) : ϕ 0 x = x := ϕ.map_zero' x /-- Iterations of a continuous function from a topological space `α` to itself defines a semiflow by `ℕ` on `α`. -/ def fromIter {g : α → α} (h : Continuous g) : Flow ℕ α where - toFun n x := g^[n] x - cont' := continuous_prod_of_discrete_left.mpr (Continuous.iterate h) + toFun n := g^[n] + cont' := continuous_prod_of_discrete_left.mpr h.iterate map_add' := iterate_add_apply _ map_zero' _x := rfl +@[simp] +theorem fromIter_apply {g : α → α} (h : Continuous g) (n : ℕ) (x : α) : + fromIter h n x = g^[n] x := rfl + /-- Restriction of a flow onto an invariant set. -/ -def restrict {s : Set α} (h : IsInvariant ϕ s) : Flow τ (↥s) where +def restrict {s : Set α} (h : IsInvariant ϕ s) : Flow τ s where toFun t := (h t).restrict _ _ _ cont' := Continuous.subtype_mk (by fun_prop) _ map_add' _ _ _ := Subtype.ext (map_add _ _ _ _) @@ -148,18 +158,23 @@ def restrict {s : Set α} (h : IsInvariant ϕ s) : Flow τ (↥s) where theorem coe_restrict_apply {s : Set α} (h : IsInvariant ϕ s) (t : τ) (x : s) : restrict ϕ h t x = ϕ t x := rfl -set_option linter.style.whitespace false in -- manual alignment is not recognised +end AddZero + +section AddMonoid + +variable [AddMonoid τ] (ϕ : Flow τ α) + /-- Convert a flow to an additive monoid action. -/ @[implicit_reducible] def toAddAction : AddAction τ α where - vadd := ϕ - add_vadd := ϕ.map_add' + vadd := ϕ + add_vadd := ϕ.map_add' zero_vadd := ϕ.map_zero' /-- Restrict a flow by `τ` to a flow by an additive submonoid of `τ`. -/ def restrictAddSubmonoid (S : AddSubmonoid τ) : Flow S α where toFun t x := ϕ t x - cont' := ϕ.continuous (continuous_subtype_val.comp continuous_fst) continuous_snd + cont' := by fun_prop map_add' t₁ t₂ x := ϕ.map_add' t₁ t₂ x map_zero' := ϕ.map_zero' @@ -249,12 +264,24 @@ theorem IsFactorOf.trans (h₁ : IsFactorOf ϕ ψ) (h₂ : IsFactorOf ψ χ) : I /-- Every flow is a factor of itself. -/ theorem IsFactorOf.self : IsFactorOf ϕ ϕ := ⟨id, (isSemiconjugacy_id_iff_eq ϕ ϕ).mpr rfl⟩ -end Flow +end AddMonoid -namespace Flow +section AddGroup -variable {τ : Type*} [AddCommGroup τ] [TopologicalSpace τ] [IsTopologicalAddGroup τ] - {α : Type*} [TopologicalSpace α] (ϕ : Flow τ α) +variable [AddGroup τ] (ϕ : Flow τ α) + +/-- The map `ϕ t` as a homeomorphism. -/ +def toHomeomorph (t : τ) : (α ≃ₜ α) where + toFun := ϕ t + invFun := ϕ (-t) + left_inv x := by simp [← map_add] + right_inv x := by simp [← map_add] + +@[simp] +theorem toHomeomorph_apply (t : τ) (x : α) : ϕ.toHomeomorph t x = ϕ t x := rfl + +@[simp] +theorem toHomeomorph_symm_apply (t : τ) (x : α) : (ϕ.toHomeomorph t).symm x = ϕ (-t) x := rfl theorem isInvariant_iff_image_eq (s : Set α) : IsInvariant ϕ s ↔ ∀ t, ϕ t '' s = s := (isInvariant_iff_image _ _).trans @@ -262,26 +289,26 @@ theorem isInvariant_iff_image_eq (s : Set α) : IsInvariant ϕ s ↔ ∀ t, ϕ t (fun h t => Subset.antisymm (h t) fun _ hx => ⟨_, h (-t) ⟨_, hx, rfl⟩, by simp [← map_add]⟩) fun h t => by rw [h t]) +theorem image_eq_preimage_symm (t : τ) (s : Set α) : ϕ t '' s = ϕ (-t) ⁻¹' s := + (ϕ.toHomeomorph t).toEquiv.image_eq_preimage_symm s + +end AddGroup + +section SubtractionCommMonoid + +variable [SubtractionCommMonoid τ] [ContinuousNeg τ] (ϕ : Flow τ α) + /-- The time-reversal of a flow `ϕ` by a (commutative, additive) group is defined `ϕ.reverse t x = ϕ (-t) x`. -/ def reverse : Flow τ α where toFun t := ϕ (-t) - cont' := ϕ.continuous continuous_fst.neg continuous_snd + cont' := by fun_prop map_add' _ _ _ := by rw [neg_add, map_add] map_zero' _ := by rw [neg_zero, map_zero_apply] -@[continuity, fun_prop] -theorem continuous_toFun (t : τ) : Continuous (ϕ.toFun t) := by - fun_prop - -/-- The map `ϕ t` as a homeomorphism. -/ -def toHomeomorph (t : τ) : (α ≃ₜ α) where - toFun := ϕ t - invFun := ϕ (-t) - left_inv x := by rw [← map_add, neg_add_cancel, map_zero_apply] - right_inv x := by rw [← map_add, add_neg_cancel, map_zero_apply] +@[simp] +theorem reverse_apply (t : τ) (x : α) : ϕ.reverse t x = ϕ (-t) x := rfl -theorem image_eq_preimage_symm (t : τ) (s : Set α) : ϕ t '' s = ϕ (-t) ⁻¹' s := - (ϕ.toHomeomorph t).toEquiv.image_eq_preimage_symm s +end SubtractionCommMonoid end Flow diff --git a/Mathlib/Dynamics/OmegaLimit.lean b/Mathlib/Dynamics/OmegaLimit.lean index 2054de1b5f9e50..870d7fcb88d7fb 100644 --- a/Mathlib/Dynamics/OmegaLimit.lean +++ b/Mathlib/Dynamics/OmegaLimit.lean @@ -306,7 +306,7 @@ end omegaLimit -/ namespace Flow -variable {τ : Type*} [TopologicalSpace τ] [AddMonoid τ] [ContinuousAdd τ] {α : Type*} +variable {τ : Type*} [TopologicalSpace τ] [AddMonoid τ] {α : Type*} [TopologicalSpace α] (f : Filter τ) (ϕ : Flow τ α) (s : Set α) open omegaLimit @@ -329,7 +329,7 @@ end Flow -/ namespace Flow -variable {τ : Type*} [TopologicalSpace τ] [AddCommGroup τ] [IsTopologicalAddGroup τ] {α : Type*} +variable {τ : Type*} [TopologicalSpace τ] [AddCommGroup τ] {α : Type*} [TopologicalSpace α] (f : Filter τ) (ϕ : Flow τ α) (s : Set α) open omegaLimit diff --git a/Mathlib/Dynamics/TopologicalEntropy/CoverEntropy.lean b/Mathlib/Dynamics/TopologicalEntropy/CoverEntropy.lean index 57ebe6ab8a6807..346c6c706000b4 100644 --- a/Mathlib/Dynamics/TopologicalEntropy/CoverEntropy.lean +++ b/Mathlib/Dynamics/TopologicalEntropy/CoverEntropy.lean @@ -11,6 +11,7 @@ public import Mathlib.Dynamics.TopologicalEntropy.DynamicalEntourage /-! # Topological entropy via covers + We implement Bowen-Dinaburg's definitions of the topological entropy, via covers. All is stated in the vocabulary of uniform spaces. For compact spaces, the uniform structure diff --git a/Mathlib/Dynamics/TopologicalEntropy/NetEntropy.lean b/Mathlib/Dynamics/TopologicalEntropy/NetEntropy.lean index b466bb29c842c9..853778648ef964 100644 --- a/Mathlib/Dynamics/TopologicalEntropy/NetEntropy.lean +++ b/Mathlib/Dynamics/TopologicalEntropy/NetEntropy.lean @@ -9,6 +9,7 @@ public import Mathlib.Dynamics.TopologicalEntropy.CoverEntropy /-! # Topological entropy via nets + We implement Bowen-Dinaburg's definitions of the topological entropy, via nets. The major design decisions are the same as in diff --git a/Mathlib/Dynamics/TopologicalEntropy/Semiconj.lean b/Mathlib/Dynamics/TopologicalEntropy/Semiconj.lean index 2dda7201c9e185..5ab181c92b7d3c 100644 --- a/Mathlib/Dynamics/TopologicalEntropy/Semiconj.lean +++ b/Mathlib/Dynamics/TopologicalEntropy/Semiconj.lean @@ -9,6 +9,7 @@ public import Mathlib.Dynamics.TopologicalEntropy.CoverEntropy /-! # Topological entropy of the image of a set under a semiconjugacy + Consider two dynamical systems `(X, S)` and `(Y, T)` together with a semiconjugacy `φ`: diff --git a/Mathlib/FieldTheory/Extension.lean b/Mathlib/FieldTheory/Extension.lean index 7221fe4410be1e..7406aef78c527b 100644 --- a/Mathlib/FieldTheory/Extension.lean +++ b/Mathlib/FieldTheory/Extension.lean @@ -59,7 +59,7 @@ noncomputable instance : OrderBot (Lifts F E K) where bot := ⟨⊥, (Algebra.ofId F K).comp (botEquiv F E)⟩ bot_le L := ⟨bot_le, fun x ↦ by obtain ⟨x, rfl⟩ := (botEquiv F E).symm.surjective x - simp_rw [AlgHom.comp_apply, AlgEquiv.coe_algHom, AlgEquiv.apply_symm_apply] + simp_rw [AlgHom.comp_apply, AlgEquiv.coe_toAlgHom, AlgEquiv.apply_symm_apply] exact L.emb.commutes x⟩ noncomputable instance : Inhabited (Lifts F E K) := diff --git a/Mathlib/FieldTheory/Galois/Basic.lean b/Mathlib/FieldTheory/Galois/Basic.lean index 878605b075d283..1f888afb4d915f 100644 --- a/Mathlib/FieldTheory/Galois/Basic.lean +++ b/Mathlib/FieldTheory/Galois/Basic.lean @@ -8,6 +8,7 @@ module public import Mathlib.FieldTheory.Fixed public import Mathlib.FieldTheory.Normal.Closure public import Mathlib.FieldTheory.PrimitiveElement +public import Mathlib.FieldTheory.SeparableClosure public import Mathlib.GroupTheory.GroupAction.FixingSubgroup /-! @@ -180,7 +181,7 @@ instance isGalois_bot : IsGalois F (⊥ : IntermediateField F E) := (IntermediateField.botEquiv F E).transfer_galois.mpr (IsGalois.self F) theorem IsGalois.of_equiv_equiv {M N : Type*} [Field N] [Field M] [Algebra M N] - [Algebra.IsAlgebraic F E] [h : IsGalois F E] {f : F ≃+* M} {g : E ≃+* N} + [h : IsGalois F E] {f : F ≃+* M} {g : E ≃+* N} (hcomp : (algebraMap M N).comp f = (g : E →+* N).comp (algebraMap F E)) : IsGalois M N := isGalois_iff.mpr ⟨Algebra.IsSeparable.of_equiv_equiv f g hcomp, Normal.of_equiv_equiv hcomp⟩ @@ -287,7 +288,7 @@ A subgroup is isomorphic to the Galois group of its fixed field. -/ def subgroupEquivAlgEquiv [FiniteDimensional F E] (H : Subgroup Gal(E/F)) : H ≃* Gal(E/IntermediateField.fixedField H) := - (MulEquiv.subgroupCongr (fixingSubgroup_fixedField H).symm).trans (fixingSubgroupEquiv _) + (MulEquiv.subgroupCongr (fixingSubgroup_fixedField H).symm).trans (fixingSubgroupEquiv _) instance fixedField.smul : SMul K (fixedField (fixingSubgroup K)) where smul x y := ⟨x * y, fun ϕ => by @@ -446,7 +447,7 @@ open scoped Pointwise theorem map_fixingSubgroup (σ : Gal(L/K)) : (E.map σ).fixingSubgroup = (MulAut.conj σ) • E.fixingSubgroup := by ext τ - simp only [coe_map, AlgEquiv.coe_algHom, Set.mem_image, SetLike.mem_coe, AlgEquiv.smul_def, + simp only [coe_map, AlgEquiv.coe_toAlgHom, Set.mem_image, SetLike.mem_coe, AlgEquiv.smul_def, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂, Subgroup.mem_pointwise_smul_iff_inv_smul_mem, ← symm_apply_eq, IntermediateField.fixingSubgroup, mem_fixingSubgroup_iff] @@ -502,6 +503,8 @@ theorem of_card_aut_eq_finrank [FiniteDimensional F E] variable {F} {E} variable {p : F[X]} +@[deprecated "No replacement; this was an auxiliary lemma used to prove \ +`Algebra.isSeparable_of_separable_splitting_field`." (since := "2026-06-12")] theorem of_separable_splitting_field_aux [hFE : FiniteDimensional F E] [sp : p.IsSplittingField F E] (hp : p.Separable) (K : Type*) [Field K] [Algebra F K] [Algebra K E] [IsScalarTower F K E] {x : E} (hx : x ∈ p.aroots E) : @@ -532,37 +535,10 @@ theorem of_separable_splitting_field_aux [hFE : FiniteDimensional F E] [sp : p.I · apply sp.splits.of_dvd (Polynomial.map_ne_zero h1) rwa [← f.comp_algebraMap, ← p.map_map, RingHom.algebraMap_toAlgebra, Polynomial.map_dvd_map'] -theorem of_separable_splitting_field [sp : p.IsSplittingField F E] (hp : p.Separable) : - IsGalois F E := by - haveI hFE : FiniteDimensional F E := Polynomial.IsSplittingField.finiteDimensional E p - letI := Classical.decEq E - let s := p.rootSet E - have adjoin_root : IntermediateField.adjoin F s = ⊤ := by - apply IntermediateField.toSubalgebra_injective - rw [IntermediateField.top_toSubalgebra, ← top_le_iff, ← sp.adjoin_rootSet] - apply IntermediateField.algebra_adjoin_le_adjoin - let P : IntermediateField F E → Prop := fun K => Nat.card (K →ₐ[F] E) = finrank F K - suffices P (IntermediateField.adjoin F s) by - rw [adjoin_root] at this - apply of_card_aut_eq_finrank - rw [← Eq.trans this (LinearEquiv.finrank_eq IntermediateField.topEquiv.toLinearEquiv)] - exact Nat.card_congr ((algEquivEquivAlgHom F E).toEquiv.trans - (IntermediateField.topEquiv.symm.arrowCongr AlgEquiv.refl)) - apply IntermediateField.induction_on_adjoin_finset _ P - · have key := IntermediateField.card_algHom_adjoin_integral F (K := E) - (show IsIntegral F (0 : E) from isIntegral_zero) - rw [IsSeparable, minpoly.zero, Polynomial.natDegree_X] at key - specialize key Polynomial.separable_X (Polynomial.Splits.X.map (algebraMap F E)) - rw [← @Subalgebra.finrank_bot F E _ _ _, ← IntermediateField.bot_toSubalgebra] at key - refine Eq.trans ?_ key - apply Nat.card_congr - rw [IntermediateField.adjoin_zero] - intro K x hx hK - simp only [P] at * - rw [of_separable_splitting_field_aux hp K (Multiset.mem_toFinset.mp hx), hK, finrank_mul_finrank] - symm - refine LinearEquiv.finrank_eq ?_ - rfl +theorem of_separable_splitting_field [p.IsSplittingField F E] (hp : p.Separable) : + IsGalois F E := + { to_isSeparable := Algebra.isSeparable_of_separable_splitting_field F E hp, + to_normal := Normal.of_isSplittingField p } /-- Equivalent characterizations of a Galois extension of finite degree. -/ theorem tfae [FiniteDimensional F E] : List.TFAE [ diff --git a/Mathlib/FieldTheory/Galois/GaloisClosure.lean b/Mathlib/FieldTheory/Galois/GaloisClosure.lean index b08c80a7d8ed8a..dfc8954599d3c3 100644 --- a/Mathlib/FieldTheory/Galois/GaloisClosure.lean +++ b/Mathlib/FieldTheory/Galois/GaloisClosure.lean @@ -5,8 +5,7 @@ Authors: Nailin Guan, Yuyang Zhao -/ module -public import Mathlib.FieldTheory.Normal.Closure -public import Mathlib.FieldTheory.SeparableClosure +public import Mathlib.FieldTheory.Galois.Basic /-! diff --git a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean index dc2cecbf723157..718a1f72439570 100644 --- a/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean +++ b/Mathlib/FieldTheory/Galois/IsGaloisGroup.lean @@ -7,14 +7,14 @@ module public import Mathlib.FieldTheory.Galois.Infinite public import Mathlib.NumberTheory.NumberField.Basic -public import Mathlib.RingTheory.Invariant.Basic +public import Mathlib.RingTheory.IsGaloisGroup.Basic /-! -# Predicate for Galois Groups +# Galois Groups of Fields -Given an action of a group `G` on an extension of fields `L/K`, we introduce a predicate -`IsGaloisGroup G K L` saying that `G` acts faithfully on `L` with fixed field `K`. In particular, -we do not assume that `L` is an algebraic extension of `K`. +Given an action of a group `G` on an extension of fields `L/K`, the predicate `IsGaloisGroup G K L` +states that `G` acts faithfully on `L` with fixed field `K`. In particular, we do not assume that +`L` is an algebraic extension of `K`. ## Implementation notes @@ -40,217 +40,8 @@ extensions of rings `B/A` seems to outweigh these terminological issues. open Module -section CommRing - -variable (G A A' B : Type*) [Group G] [CommSemiring A] [Semiring B] [Algebra A B] - [MulSemiringAction G B] - -/-- `G` is a Galois group for `L/K` if the action of `G` on `L` is faithful with fixed field `K`. -In particular, we do not assume that `L` is an algebraic extension of `K`. - -See the implementation notes in this file for the meaning of this definition in the case of rings. --/ -class IsGaloisGroup where - faithful : FaithfulSMul G B - commutes : SMulCommClass G A B - isInvariant : Algebra.IsInvariant A B G - -variable {G A B} in -theorem IsGaloisGroup.of_mulEquiv [hG : IsGaloisGroup G A B] {H : Type*} [Group H] - [MulSemiringAction H B] (e : H ≃* G) (he : ∀ h (x : B), (e h) • x = h • x) : - IsGaloisGroup H A B where - faithful := ⟨fun h ↦ e.injective <| hG.faithful.eq_of_smul_eq_smul <| by simpa only [he]⟩ - commutes := ⟨fun x a b ↦ by simpa [he] using hG.commutes.smul_comm (e x) a b⟩ - isInvariant := ⟨fun b h ↦ - have he' : ∀ (g : G) (x : B), e.symm g • x = g • x := fun g x ↦ by simp [← he] - hG.isInvariant.isInvariant b (fun g ↦ by simpa [he'] using h (e.symm g))⟩ - -variable {G A B} in -theorem IsGaloisGroup.iff_of_mulEquiv {H : Type*} [Group H] [MulSemiringAction H B] - (e : H ≃* G) (he : ∀ h (x : B), e h • x = h • x) : - IsGaloisGroup H A B ↔ IsGaloisGroup G A B := by - refine ⟨fun h ↦ h.of_mulEquiv e.symm fun g x ↦ ?_, fun h ↦ h.of_mulEquiv e he⟩ - rw [← he, e.apply_symm_apply] - -variable {G A B} in -@[simp] -theorem IsGaloisGroup.top_iff : IsGaloisGroup (⊤ : Subgroup G) A B ↔ IsGaloisGroup G A B := - iff_of_mulEquiv Subgroup.topEquiv fun _ _ ↦ rfl - -instance [IsGaloisGroup G A B] : IsGaloisGroup (⊤ : Subgroup G) A B := - IsGaloisGroup.top_iff.mpr ‹_› - -theorem IsGaloisGroup.of_algEquiv [hG : IsGaloisGroup G A B] (B' : Type*) [Semiring B'] - [Algebra A B'] [MulSemiringAction G B'] (e : B ≃ₐ[A] B') - (he : ∀ (g : G) (x : B), e (g • x) = g • (e x)) : - IsGaloisGroup G A B' where - faithful := ⟨fun h ↦ hG.faithful.eq_of_smul_eq_smul fun b ↦ by simpa [← he] using h (e b)⟩ - commutes := ⟨fun g a b' ↦ by - have h' {x'} : e.symm (g • x') = g • e.symm x' := by - apply e.injective - simp [he] - apply e.symm.injective - simpa [h', map_smul] using hG.commutes.smul_comm g a (e.symm b')⟩ - isInvariant := ⟨fun x' hx' ↦ by - obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant (e.symm x') (fun g ↦ by - apply e.injective - simp [he, hx']) - exact ⟨a, by rw [← e.commutes, ha, AlgEquiv.apply_symm_apply]⟩⟩ - -theorem IsGaloisGroup.of_ringEquiv [hG : IsGaloisGroup G A B] [CommSemiring A'] [Algebra A' B] - (e : A ≃+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a) : - IsGaloisGroup G A' B where - faithful := hG.faithful - commutes := ⟨by - intro g a' b - obtain ⟨a, rfl⟩ : ∃ a, e a = a' := e.surjective a' - rw [Algebra.smul_def, Algebra.smul_def, he, ← Algebra.smul_def, ← Algebra.smul_def] - exact hG.commutes.smul_comm g a b⟩ - isInvariant := ⟨by - intro b h - obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant b h - exact ⟨e a, by rw [he, ha]⟩⟩ - -attribute [instance low] IsGaloisGroup.commutes IsGaloisGroup.isInvariant - -variable {C : Type*} [CommSemiring C] [Algebra C B] - -variable {G} in -protected theorem Subgroup.smul_algebraMap {H : Subgroup G} [SMulCommClass H C B] {g : G} - (hg : g ∈ H) (x : C) : - g • algebraMap C B x = algebraMap C B x := - smul_algebraMap (⟨g, hg⟩ : H) x - -theorem IsGaloisGroup.smul_mem_of_normal (N : Subgroup G) [hN : N.Normal] - [hC : IsGaloisGroup N C B] (g : G) (x : C) : - g • algebraMap C B x ∈ Set.range (algebraMap C B) := by - apply hC.isInvariant.isInvariant (g • algebraMap C B x) - intro n - rw [← inv_smul_eq_iff, Subgroup.smul_def, ← mul_smul, ← mul_smul] - exact Subgroup.smul_algebraMap B (hN.conj_mem' n n.prop g) x - -@[deprecated (since := "2026-05-28")] alias smul_eq_self := Subgroup.smul_algebraMap -@[deprecated (since := "2026-05-28")] alias smul_mem_of_normal := IsGaloisGroup.smul_mem_of_normal - -variable [hA : IsGaloisGroup G A B] [FaithfulSMul A B] - -/-- -If `B/A` is Galois with Galois group `G`, then `A` is isomorphic to the subring of elements of `B` -fixed by `G`. --/ -@[simps apply_coe] -noncomputable def IsGaloisGroup.ringEquivFixedPoints : - A ≃+* FixedPoints.subsemiring B G where - toFun x := ⟨algebraMap A B x, fun _ ↦ by rw [smul_algebraMap]⟩ - invFun x := (hA.isInvariant.isInvariant x x.prop).choose - map_mul' _ _ := by simp [Subtype.ext_iff] - map_add' _ _ := by simp [Subtype.ext_iff] - left_inv _ := by simp - right_inv x := by simpa [Subtype.ext_iff] using (hA.isInvariant.isInvariant x x.prop).choose_spec - -@[simp] -theorem IsGaloisGroup.algebraMap_ringEquivFixedPoints_symm_apply (x : FixedPoints.subsemiring B G) : - algebraMap A B ((ringEquivFixedPoints G A B).symm x) = x := - (hA.isInvariant.isInvariant x x.prop).choose_spec - -variable [CommSemiring A'] [Algebra A' B] [FaithfulSMul A' B] [hA' : IsGaloisGroup G A' B] - -/-- -If `B/A` and `B/A'` are Galois with the same Galois group, then `A ≃+* A'`. --/ -noncomputable def IsGaloisGroup.ringEquiv : - A ≃+* A' := - (ringEquivFixedPoints G A B).trans (ringEquivFixedPoints G A' B).symm - -@[simp] -theorem IsGaloisGroup.algebraMap_ringEquiv_apply (x : A) : - algebraMap A' B (IsGaloisGroup.ringEquiv G A A' B x) = algebraMap A B x := by - simp [ringEquiv] - -@[simp] -theorem IsGaloisGroup.algebraMap_ringEquiv_symm_apply (x : A') : - algebraMap A B ((IsGaloisGroup.ringEquiv G A A' B).symm x) = algebraMap A' B x := by - simp [ringEquiv] - -end CommRing - section Field -variable (G A B K L : Type*) [Group G] [CommRing A] [CommRing B] [MulSemiringAction G B] - [Algebra A B] [Field K] [Field L] [Algebra K L] [Algebra A K] [Algebra B L] [Algebra A L] - [IsFractionRing A K] [IsFractionRing B L] [IsScalarTower A K L] [IsScalarTower A B L] - [MulSemiringAction G L] [SMulDistribClass G B L] - -instance [IsGaloisGroup G A B] : IsGaloisGroup G (algebraMap A B).range B where - faithful := IsGaloisGroup.faithful A - commutes := ⟨fun g ⟨a', ⟨a, ha⟩⟩ b ↦ by simp [Subring.smul_def, ← ha]⟩ - isInvariant := ⟨fun b hb ↦ by - obtain ⟨a, ha⟩ := Algebra.IsInvariant.isInvariant (A := A) b hb - exact ⟨⟨algebraMap A B a, ⟨a, rfl⟩⟩, ha⟩⟩ - -/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/ -theorem IsGaloisGroup.to_isFractionRing_of_isIntegral - [Algebra.IsIntegral A B] [hGAB : IsGaloisGroup G A B] : - IsGaloisGroup G K L where - faithful := - have := hGAB.faithful - IsFractionRing.faithfulSMul G B L - commutes := IsFractionRing.smulCommClass G A B K L - isInvariant := IsFractionRing.isInvariant_of_isIntegral G A B K L - -/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/ -theorem IsGaloisGroup.to_isFractionRing [Finite G] [hGAB : IsGaloisGroup G A B] : - IsGaloisGroup G K L := - have := hGAB.isInvariant.isIntegral - IsGaloisGroup.to_isFractionRing_of_isIntegral G A B K L - -/-- If `B` is an integral extension of an integrally closed domain `A`, then `IsGaloisGroup` for -their fraction fields implies `IsGaloisGroup` for these rings. -/ -theorem IsGaloisGroup.of_isFractionRing [hGKL : IsGaloisGroup G K L] - [IsIntegrallyClosed A] [Algebra.IsIntegral A B] : IsGaloisGroup G A B := by - have hc (a : A) : (algebraMap K L) (algebraMap A K a) = (algebraMap B L) (algebraMap A B a) := by - simp_rw [← IsScalarTower.algebraMap_apply] - refine ⟨⟨fun h ↦ ?_⟩, ⟨fun g x y ↦ IsFractionRing.injective B L ?_⟩, ⟨fun x h ↦ ?_⟩⟩ - · have := hGKL.faithful - refine eq_of_smul_eq_smul fun (y : L) ↦ ?_ - obtain ⟨a, b, hb, rfl⟩ := IsFractionRing.div_surjective B y - simp only [smul_div₀', ← algebraMap.coe_smul', h] - · simp [Algebra.smul_def, algebraMap.coe_smul', ← hc] - · obtain ⟨b, hb⟩ := hGKL.isInvariant.isInvariant (algebraMap B L x) - (by simpa [← algebraMap.coe_smul']) - have hx : IsIntegral A (algebraMap B L x) := (Algebra.IsIntegral.isIntegral x).algebraMap - rw [← hb, isIntegral_algebraMap_iff (algebraMap K L).injective, - IsIntegrallyClosedIn.isIntegral_iff] at hx - obtain ⟨a, rfl⟩ := hx - exact ⟨a, by rwa [hc, IsFractionRing.coe_inj] at hb⟩ - -/-- If `G` is finite and `A` is integrally closed then `IsGaloisGroup G A B` is equivalent to `B/A` -being integral and the fields of fractions `Frac(B)/Frac(A)` being Galois with Galois group `G`. -/ -theorem IsGaloisGroup.iff_isFractionRing [Finite G] [IsIntegrallyClosed A] : - IsGaloisGroup G A B ↔ Algebra.IsIntegral A B ∧ IsGaloisGroup G K L := - ⟨fun h ↦ ⟨h.isInvariant.isIntegral, h.to_isFractionRing G A B K L⟩, - fun ⟨_, h⟩ ↦ h.of_isFractionRing G A B K L⟩ - -@[deprecated (since := "2026-04-20")] alias FractionRing.mulSemiringAction_of_isGaloisGroup := - IsFractionRing.mulSemiringAction - -attribute [local instance] FractionRing.liftAlgebra in -/-- -If `G` is finite and `IsGaloisGroup G A B` with `A` and `B` domains, then `G` is also -a Galois group for `FractionRing B / FractionRing A` for the action defined by -`IsFractionRing.mulSemiringAction`. --/ -theorem IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [IsTorsionFree A B] [Finite G] - [IsGaloisGroup G A B] : - letI := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B) - IsGaloisGroup G (FractionRing A) (FractionRing B) := by - let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B) - have : SMulDistribClass G B (FractionRing B) := ⟨fun g b x ↦ by - rw [Algebra.smul_def', Algebra.smul_def', smul_mul'] - congr - exact IsFractionRing.fieldEquivOfAlgEquiv_algebraMap (FractionRing A) _ _ _ b⟩ - apply IsGaloisGroup.to_isFractionRing G A B _ _ - open NumberField instance (K L : Type*) [Field K] [Field L] [NumberField K] [NumberField L] [Algebra K L] @@ -306,10 +97,10 @@ theorem finiteDimensional [Finite G] [IsGaloisGroup G K L] : FiniteDimensional K FiniteDimensional.of_finrank_pos (card_eq_finrank G K L ▸ Nat.card_pos) protected theorem finite (R B : Type*) [CommRing R] [CommRing B] [Algebra R B] [Module.Finite R B] - [IsDomain B] [MulSemiringAction G B] [IsGaloisGroup G R B] : Finite G := by + [IsDomain B] [MulSemiringAction G B] [IsGaloisGroup G R B] : Finite G := by let A : Subring B := (algebraMap R B).range let := FractionRing.liftAlgebra A (FractionRing B) - let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B) + let := IsFractionRing.mulSemiringAction G B (FractionRing B) let : Algebra R A := (algebraMap R B).rangeRestrict.toAlgebra have : IsScalarTower R A B := IsScalarTower.of_algebraMap_eq' rfl have : Module.Finite A B := Module.Finite.of_restrictScalars_finite R A B @@ -318,95 +109,54 @@ protected theorem finite (R B : Type*) [CommRing R] [CommRing B] [Algebra R B] [ rw [card_eq_finrank G (FractionRing A) (FractionRing B)] exact Module.finrank_pos.ne' +section IsDomain + +variable (A B : Type*) [CommRing A] [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] + [MulSemiringAction G B] [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B] + [Finite G] [Finite G'] + /-- The cardinality of a Galois group of `B/A` equals the rank of `B` as an `A`-module. -See `IsGaloisGroup.card_eq_finrank` a field-theoretic version that does not assume finiteness. -/ -theorem card_eq_finrank' (A B : Type*) [CommRing A] [CommRing B] [Algebra A B] [Finite G] - [IsDomain B] [FaithfulSMul A B] [MulSemiringAction G B] [IsGaloisGroup G A B] : - Nat.card G = Module.finrank A B := by +See `IsGaloisGroup.card_eq_finrank`, a field-theoretic version that does not assume finiteness. -/ +theorem card_eq_finrank' : Nat.card G = Module.finrank A B := by have := IsDomain.of_faithfulSMul A B let := FractionRing.liftAlgebra A (FractionRing B) - let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B) + let := IsFractionRing.mulSemiringAction G B (FractionRing B) have : Algebra.IsIntegral A B := IsGaloisGroup.isInvariant.isIntegral A B G - rw [(IsGaloisGroup.toFractionRing G A B).card_eq_finrank, + rw [IsGaloisGroup.card_eq_finrank G (FractionRing A) (FractionRing B), Algebra.IsAlgebraic.finrank_of_isFractionRing A (FractionRing A) B (FractionRing B)] -/-- If `G` is a finite Galois group for `L/K`, then `G` is isomorphic to `Gal(L/K)`. -/ -@[simps!] noncomputable def mulEquivAlgEquiv [IsGaloisGroup G K L] [Finite G] : G ≃* Gal(L/K) := - MulEquiv.ofBijective (MulSemiringAction.toAlgAut G K L) (by - have := isGalois G K L - have := finiteDimensional G K L - rw [Nat.bijective_iff_injective_and_card, card_eq_finrank G K L, - IsGalois.card_aut_eq_finrank K L] - exact ⟨fun _ _ ↦ (faithful K).eq_of_smul_eq_smul ∘ DFunLike.ext_iff.mp, rfl⟩) - @[simp] -theorem map_mulEquivAlgEquiv_fixingSubgroup - [IsGaloisGroup G K L] [Finite G] (F : IntermediateField K L) : +theorem map_mulEquivAlgEquiv_fixingSubgroup [IsGaloisGroup G K L] (F : IntermediateField K L) : (fixingSubgroup G (F : Set L)).map (mulEquivAlgEquiv G K L) = F.fixingSubgroup := by ext g obtain ⟨g, rfl⟩ := (mulEquivAlgEquiv G K L).surjective g simp [mem_fixingSubgroup_iff] -/-- If `G` and `G'` are finite Galois groups for `L/K`, then `G` is isomorphic to `G'`. -See `mulEquivCongr` for a more general version. -/ -noncomputable def mulEquivCongr' [IsGaloisGroup G K L] [Finite G] - [IsGaloisGroup G' K L] [Finite G'] : G ≃* G' := - (mulEquivAlgEquiv G K L).trans (mulEquivAlgEquiv G' K L).symm +/-- If `G` and `G'` are finite Galois groups for `B/A`, then `G` is isomorphic to `G'`. -/ +noncomputable def mulEquivCongr : G ≃* G' := + (mulEquivAlgEquiv G A B).trans (mulEquivAlgEquiv G' A B).symm @[simp] -theorem mulEquivCongr'_apply_smul [IsGaloisGroup G K L] [Finite G] [IsGaloisGroup G' K L] - [Finite G'] (g : G) (x : L) : mulEquivCongr' G G' K L g • x = g • x := - AlgEquiv.ext_iff.mp ((mulEquivAlgEquiv G' K L).apply_symm_apply (mulEquivAlgEquiv G K L g)) x - -attribute [local instance] FractionRing.liftAlgebra in -/-- If `G` and `G'` are finite Galois groups for `B/A` with `B` a domain, then `G` is -isomorphic to `G'`. -/ -noncomputable def mulEquivCongr [Finite G] [Finite G'] (A B : Type*) [CommRing A] - [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] [MulSemiringAction G B] - [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B] : - G ≃* G' := - haveI : IsDomain A := (FaithfulSMul.algebraMap_injective A B).isDomain - letI K := FractionRing A - letI L := FractionRing B - letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G A B K L - letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' A B K L - haveI : IsGaloisGroup G K L := IsGaloisGroup.toFractionRing G A B - haveI : IsGaloisGroup G' K L := IsGaloisGroup.toFractionRing G' A B - mulEquivCongr' G G' K L - -attribute [local instance] FractionRing.liftAlgebra in -@[simp] -theorem mulEquivCongr_apply_smul [Finite G] [Finite G'] (A B : Type*) [CommRing A] - [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] [MulSemiringAction G B] - [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B] (g : G) (x : B) : - mulEquivCongr G G' A B g • x = g • x := by - haveI : IsDomain A := (FaithfulSMul.algebraMap_injective A B).isDomain - letI K := FractionRing A - letI L := FractionRing B - letI : MulSemiringAction G L := IsFractionRing.mulSemiringAction G A B K L - letI : MulSemiringAction G' L := IsFractionRing.mulSemiringAction G' A B K L - haveI : IsGaloisGroup G K L := IsGaloisGroup.toFractionRing G A B - haveI : IsGaloisGroup G' K L := IsGaloisGroup.toFractionRing G' A B - apply FaithfulSMul.algebraMap_injective B L - rw [algebraMap.smul', algebraMap.smul'] - exact mulEquivCongr'_apply_smul G G' K L g _ +theorem mulEquivCongr_apply_smul (g : G) (x : B) : mulEquivCongr G G' A B g • x = g • x := + AlgEquiv.ext_iff.mp ((mulEquivAlgEquiv G' A B).apply_symm_apply (mulEquivAlgEquiv G A B g)) x @[simp] -theorem mulEquivCongr_symm_apply_smul [Finite G] [Finite G'] (A B : Type*) [CommRing A] - [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] [MulSemiringAction G B] - [MulSemiringAction G' B] [IsGaloisGroup G A B] [IsGaloisGroup G' A B] (g : G') (x : B) : +theorem mulEquivCongr_symm_apply_smul (g : G') (x : B) : (mulEquivCongr G G' A B).symm g • x = g • x := by rw [← mulEquivCongr_apply_smul G G' A B, MulEquiv.apply_symm_apply] -variable (H H' : Subgroup G) (F F' : IntermediateField K L) +@[deprecated (since := "2026-06-19")] alias mulEquivCongr' := mulEquivCongr +@[deprecated (since := "2026-06-19")] alias mulEquivCongr'_apply_smul := mulEquivCongr_apply_smul -instance (R S : Type*) [CommRing R] [CommRing S] [Algebra R S] - [MulSemiringAction G S] [hGKL : IsGaloisGroup G R S] : - IsGaloisGroup H (FixedPoints.subalgebra R S H) S where - faithful := have := hGKL.faithful; inferInstance - commutes := inferInstance - isInvariant := ⟨fun x h ↦ ⟨⟨x, h⟩, rfl⟩⟩ +theorem mulEquivCongr_mapSubgroup_fixingSubgroup (S : Set B) : + (fixingSubgroup G S).map (mulEquivCongr G G' A B) = fixingSubgroup G' S := by + ext g + simp [Subgroup.map_equiv_eq_comap_symm, mem_fixingSubgroup_iff] + +end IsDomain + +variable (H H' : Subgroup G) (F F' : IntermediateField K L) instance subgroup [hGKL : IsGaloisGroup G K L] : IsGaloisGroup H (FixedPoints.intermediateField H : IntermediateField K L) L := @@ -453,6 +203,17 @@ instance intermediateField [Finite G] [hGKL : IsGaloisGroup G K L] : have := hGKL.isGalois .of_mulEquiv_algEquiv e fun _ _ ↦ rfl +include K in +/-- If `G` is a Galois group on `L/K` and `L/E/K` is a tower of field extensions, +then the fixing subgroup of the image of `E` in `L` is a Galois group on `L/E`. -/ +theorem of_isScalarTower [Finite G] [IsGaloisGroup G K L] (E : Type*) [Field E] [Algebra K E] + [Algebra E L] [IsScalarTower K E L] : + IsGaloisGroup (fixingSubgroup G (Set.range (algebraMap E L))) E L := by + rw [← IsScalarTower.toAlgHom_fieldRange K E L] + refine IsGaloisGroup.of_ringEquiv _ _ _ L + (AlgHom.equivFieldRange (IsScalarTower.toAlgHom K E L)).toRingEquiv.symm fun ⟨_, ⟨x, rfl⟩⟩ ↦ ?_ + simp [AlgEquiv.symm_apply_eq, Subtype.ext_iff] + @[simp] theorem card_fixingSubgroup_eq_finrank [Finite G] [IsGaloisGroup G K L] : Nat.card (fixingSubgroup G (F : Set L)) = Module.finrank F L := @@ -541,7 +302,7 @@ theorem fixedPoints_fixingSubgroup [Finite G] : /-- If `G` acts as a Galois group on `L/K` and the subgroup `H` acts as a Galois group on `L/B`, then the fixed points of `H` equals the range of `algebraMap B L`. -/ -theorem fixedPoints_eq_range_algebraMap [Finite G] (B : Type*) +theorem fixedPoints_eq_range_algebraMap (B : Type*) [CommSemiring B] [Algebra B L] [IsGaloisGroup H B L] : (FixedPoints.intermediateField H : IntermediateField K L) = Set.range (algebraMap B L) := by ext @@ -571,8 +332,7 @@ theorem fixingSubgroup_range_algebraMap [Finite G] (A B C : Type*) (H : Subgroup have : IsDomain A := (FaithfulSMul.algebraMap_injective A C).isDomain let K := FractionRing A let L := FractionRing C - let : MulSemiringAction G L := IsFractionRing.mulSemiringAction G A C K L - have : IsGaloisGroup G K L := IsGaloisGroup.toFractionRing G A C + let : MulSemiringAction G L := IsFractionRing.mulSemiringAction G C L have : IsGaloisGroup H (FractionRing B) L := IsGaloisGroup.toFractionRing H B C rw [← fixingSubgroup_range_algebraMap' G K L H (FractionRing B)] ext g @@ -590,99 +350,30 @@ theorem fixingSubgroup_range_algebraMap [Finite G] (A B C : Type*) (H : Subgroup use algebraMap B (FractionRing B) x rw [← IsScalarTower.algebraMap_apply, ← IsScalarTower.algebraMap_apply] +open Pointwise in +/-- If `G` is a finite Galois group for `L/K`, `H` is a Galois group for `L/E`, and `E/K` is +Galois, then `H` is a normal subgroup of `G`. -/ +theorem normal_of_isGalois (E : Type*) [Field E] [Algebra K E] [Algebra E L] [IsScalarTower K E L] + [Finite G] [IsGaloisGroup H E L] [IsGalois K E] : H.Normal := by + let F := (IsScalarTower.toAlgHom K E L).fieldRange + have : IsGalois K F := .of_algEquiv (IsScalarTower.toAlgHom K E L).equivFieldRange + have hFL : IsGaloisGroup H F L := inferInstanceAs (IsGaloisGroup H (algebraMap E L).range L) + have := isGalois G K L + have : Finite Gal(L/K) := Finite.of_equiv _ (mulEquivAlgEquiv G K L).toEquiv + rw [← fixingSubgroup_fixedPoints G K L H, subgroup_iff.mp hFL, + ← mulEquivCongr_mapSubgroup_fixingSubgroup Gal(L/K) G K, MulEquiv.normal_map_iff] + exact IsGalois.fixingSubgroup_normal_of_isGalois F + end IsGaloisGroup end GaloisCorrespondence section Quotient -section Semiring - -variable (A B C : Type*) [CommSemiring A] [Semiring C] [Algebra A C] [MulSemiringAction G C] -variable (N : Subgroup G) [CommSemiring B] [Algebra B C] - -/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B`. -For `g : G` and `x : B`, `g • x` is the unique element of `B` whose image in `C` is -`g • algebraMap B C x`, see `algebraMap_smulOfNormal`. -/ -@[implicit_reducible] -noncomputable def smulOfNormal [N.Normal] [IsGaloisGroup N B C] : SMul G B where - smul g x := (smul_mem_of_normal G C N g x).choose - -@[simp] -theorem algebraMap_smulOfNormal [N.Normal] [IsGaloisGroup N B C] (g : G) (x : B) : - letI := smulOfNormal G B C - algebraMap B C (g • x) = g • algebraMap B C x := - (smul_mem_of_normal G C N g x).choose_spec - -/-- If `N` is normal and `IsGaloisGroup N B C`, the action `smulOfNormal G B C` satisfies -`SMulDistribClass G B C`. -/ -instance smulDistribClass_smulOfNormal [N.Normal] [IsGaloisGroup N B C] : - letI := smulOfNormal G B C - SMulDistribClass G B C := - let := smulOfNormal G B C - ⟨fun g b c ↦ by simp [Algebra.smul_def]⟩ - -variable [FaithfulSMul B C] - -/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B` as a -`MulSemiringAction`, via the action defined in `smulOfNormal`. -/ -@[implicit_reducible] -noncomputable def mulSemiringActionOfNormal [IsGaloisGroup N B C] [N.Normal] : - MulSemiringAction G B := by - let : SMul G B := smulOfNormal G B C N - have : SMulDistribClass G B C := smulDistribClass_smulOfNormal G B C N - exact mulSemiringActionOfSmulDistribClass B C G - -/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then the quotient group `G ⧸ N` -acts on `B` by `(g : G ⧸ N) • x = g • x`. -/ -@[implicit_reducible] -noncomputable def mulSemiringActionQuotient [IsGaloisGroup N B C] [N.Normal] : - MulSemiringAction (G ⧸ N) B := - letI := mulSemiringActionOfNormal G B C N - { smul q x := - Quotient.liftOn' q (· • x) fun g₁ g₂ h ↦ by - apply FaithfulSMul.algebraMap_injective B C - rw [algebraMap.smul', algebraMap.smul', smul_eq_iff_eq_inv_smul, ← smul_assoc, smul_eq_mul, - Subgroup.smul_algebraMap C (by rwa [← QuotientGroup.leftRel_apply])] - one_smul x := one_smul G x - mul_smul q₁ q₂ x := Quotient.inductionOn₂' q₁ q₂ fun g h ↦ mul_smul g h x - smul_add q x y := Quotient.inductionOn' q fun g ↦ smul_add g x y - smul_zero q := Quotient.inductionOn' q fun g ↦ smul_zero g - smul_one q := Quotient.inductionOn' q fun g ↦ smul_one g - smul_mul q x y := Quotient.inductionOn' q fun g ↦ smul_mul' g x y } - -theorem mulSemiringActionQuotient_smul_def [MulSemiringAction G B] [SMulDistribClass G B C] - [IsGaloisGroup N B C] [N.Normal] (g : G) (b : B) : - letI := mulSemiringActionQuotient G B C N - (g : G ⧸ N) • b = g • b := by - let := mulSemiringActionOfNormal G B C N - refine (Quotient.liftOn'_mk'' (· • b) _ g).trans (FaithfulSMul.algebraMap_injective B C ?_) - rw [algebraMap.smul', algebraMap.smul'] - -theorem isScalarTower_mulSemiringActionQuotient [MulSemiringAction G B] [SMulDistribClass G B C] - [IsGaloisGroup N B C] [N.Normal] : - letI := mulSemiringActionQuotient G B C N - IsScalarTower G (G ⧸ N) B := - let := mulSemiringActionQuotient G B C N - ⟨fun g q b ↦ Quotient.inductionOn' q fun h ↦ by - simp [mul_smul, mulSemiringActionQuotient_smul_def]⟩ - -/-- If `G` acts on `C` commuting with `A`, then the action of `G ⧸ N` on `B` commutes with `A`. -/ -@[implicit_reducible] -def smulCommClassQuotient [N.Normal] [Algebra A B] [IsScalarTower A B C] [SMulCommClass G A C] - [MulSemiringAction G B] [MulAction (G ⧸ N) B] [SMulDistribClass G B C] - [IsScalarTower G (G ⧸ N) B] : - SMulCommClass (G ⧸ N) A B := - ⟨fun g k x ↦ Quotient.inductionOn' g fun g ↦ - FaithfulSMul.algebraMap_injective B C (by - simp [algebraMap.smul, algebraMap.smul', smul_comm])⟩ - -end Semiring - section Domain variable (A B C : Type*) [CommRing A] [CommRing B] [CommRing C] [IsDomain C] [Algebra A B] - [Algebra A C] [Algebra B C] [FaithfulSMul A C] [FaithfulSMul B C] [IsScalarTower A B C] + [Algebra A C] [Algebra B C] [FaithfulSMul A B] [FaithfulSMul B C] [IsScalarTower A B C] /-- If `G` is a Galois group for `C/A`, and the normal subgroup `N ≤ G` is a Galois group for `C/B`, then the quotient `G ⧸ N` is a Galois group for `B/A`. -/ @@ -692,6 +383,7 @@ theorem quotient [Finite G] (N : Subgroup G) [N.Normal] [MulSemiringAction G C] [IsGaloisGroup N B C] : IsGaloisGroup (G ⧸ N) A B where faithful.eq_of_smul_eq_smul := fun {g₁} {g₂} ↦ Quotient.inductionOn₂' g₁ g₂ fun g₁ g₂ h ↦ by + have : FaithfulSMul A C := FaithfulSMul.trans A B C have h' : ∀ g : G, (∀ x : B, g • x = x) → g ∈ N := by simp [← fixingSubgroup_range_algebraMap G A B C N, mem_fixingSubgroup_iff, ← algebraMap.smul', (FaithfulSMul.algebraMap_injective B C).eq_iff] @@ -708,29 +400,98 @@ theorem quotient [Finite G] (N : Subgroup G) [N.Normal] [MulSemiringAction G C] have := (FaithfulSMul.algebraMap_injective B C).eq_iff.mpr <| h g rwa [MulAction.coe_quotient_smul, algebraMap.smul'] at this +/-- If `G` is a Galois group for `C/A`, the normal subgroup `N ≤ G` is a Galois group for `C/B`, +and `G'` is a Galois group for `B/A`, then `G ⧸ N ≃* G'`. -/ +noncomputable def quotientMulEquiv [Finite G] [Finite G'] (N : Subgroup G) [N.Normal] + [MulSemiringAction G C] [IsGaloisGroup G A C] [IsGaloisGroup N B C] [MulSemiringAction G' B] + [IsGaloisGroup G' A B] : + G ⧸ N ≃* G' := + haveI : IsDomain B := (FaithfulSMul.algebraMap_injective B C).isDomain + letI := mulSemiringActionOfNormal G B C N + letI := mulSemiringActionQuotient G B C N + haveI := smulCommClassQuotient G A B C N + haveI := quotient G A B C N + mulEquivCongr (G ⧸ N) G' A B + +@[simp] +theorem algebraMap_quotientMulEquiv_smul [Finite G] [Finite G'] (N : Subgroup G) [N.Normal] + [MulSemiringAction G C] [IsGaloisGroup G A C] [IsGaloisGroup N B C] [MulSemiringAction G' B] + [IsGaloisGroup G' A B] (g : G) (x : B) : + algebraMap B C (quotientMulEquiv G G' A B C N g • x) = g • algebraMap B C x := by + haveI : IsDomain B := (FaithfulSMul.algebraMap_injective B C).isDomain + letI := mulSemiringActionOfNormal G B C N + letI := mulSemiringActionQuotient G B C N + haveI := smulCommClassQuotient G A B C N + haveI := quotient G A B C N + rw [← algebraMap_smulOfNormal G B C N g x] + congr + apply mulEquivCongr_apply_smul + +attribute [local instance] FractionRing.liftAlgebra in +/-- The restriction homomorphism from the Galois group of `C/A` to the Galois group of `B/A` where +`C/B/A` is a tower of domains with `C/A` and `B/A` Galois. -/ +noncomputable def restrictHom [Finite G] [Finite G'] [MulSemiringAction G C] [IsGaloisGroup G A C] + [MulSemiringAction G' B] [IsGaloisGroup G' A B] : + G →* G' := + haveI : IsDomain B := IsDomain.of_faithfulSMul B C + haveI : IsDomain A := IsDomain.of_faithfulSMul A B + haveI : FaithfulSMul A C := FaithfulSMul.trans A B C + letI : MulSemiringAction G (FractionRing C) := + IsFractionRing.mulSemiringAction G C (FractionRing C) + letI N := fixingSubgroup G (Set.range (algebraMap (FractionRing B) (FractionRing C))) + haveI : IsGaloisGroup N (FractionRing B) (FractionRing C) := + of_isScalarTower G (FractionRing A) (FractionRing C) (FractionRing B) + letI : MulSemiringAction G' (FractionRing B) := + IsFractionRing.mulSemiringAction G' B (FractionRing B) + haveI := isGalois G' (FractionRing A) (FractionRing B) + haveI : N.Normal := normal_of_isGalois G (FractionRing A) (FractionRing C) N (FractionRing B) + (quotientMulEquiv G G' (FractionRing A) (FractionRing B) (FractionRing C) N).toMonoidHom.comp + (QuotientGroup.mk' N) + +attribute [local instance] FractionRing.liftAlgebra in +@[simp] +theorem algebraMap_restrictHom_smul [Finite G] [Finite G'] [MulSemiringAction G C] + [IsGaloisGroup G A C] [MulSemiringAction G' B] [IsGaloisGroup G' A B] (g : G) (x : B) : + algebraMap B C (restrictHom G G' A B C g • x) = g • algebraMap B C x := by + have : IsDomain B := IsDomain.of_faithfulSMul B C + have : IsDomain A := IsDomain.of_faithfulSMul A B + have : FaithfulSMul A C := FaithfulSMul.trans A B C + let : MulSemiringAction G (FractionRing C) := + IsFractionRing.mulSemiringAction G C (FractionRing C) + let : MulSemiringAction G' (FractionRing B) := + IsFractionRing.mulSemiringAction G' B (FractionRing B) + apply FaithfulSMul.algebraMap_injective C (FractionRing C) + rw [← IsScalarTower.algebraMap_apply, + IsScalarTower.algebraMap_apply B (FractionRing B) (FractionRing C)] + simp only [restrictHom, MulEquiv.toMonoidHom_eq_coe, MonoidHom.coe_comp, MonoidHom.coe_coe, + QuotientGroup.coe_mk', Function.comp_apply] + rw [algebraMap.smul', algebraMap_quotientMulEquiv_smul, ← IsScalarTower.algebraMap_apply, + algebraMap.smul', ← IsScalarTower.algebraMap_apply] + +attribute [local instance] FractionRing.liftAlgebra in +theorem restrictHom_surjective [Finite G] [Finite G'] [MulSemiringAction G C] + [IsGaloisGroup G A C] [MulSemiringAction G' B] [IsGaloisGroup G' A B] : + Function.Surjective (restrictHom G G' A B C) := by + simpa [restrictHom] using QuotientGroup.mk_surjective + +open Pointwise in +theorem restrictHom_smul_under [Finite G] [Finite G'] [MulSemiringAction G C] + [IsGaloisGroup G A C] [MulSemiringAction G' B] [IsGaloisGroup G' A B] (g : G) (I : Ideal C) : + restrictHom G G' A B C g • I.under B = (g • I).under B := by + simp [Ideal.ext_iff, Ideal.mem_pointwise_smul_iff_inv_smul_mem, ← map_inv] + end Domain noncomputable section IntermediateField variable (N : Subgroup G) [N.Normal] [IsGaloisGroup N F L] -instance : MulSemiringAction (G ⧸ N) F := - letI := smulOfNormal G F L N - haveI := smulDistribClass_smulOfNormal G F L N - letI := mulSemiringActionOfSmulDistribClass F L G - mulSemiringActionQuotient G F L N - -instance [SMulCommClass G K L] [MulSemiringAction G F] [SMulDistribClass G F L] - [IsScalarTower G (G ⧸ N) F] : SMulCommClass (G ⧸ N) K F := - smulCommClassQuotient G K F L N - /-- If `G` is a finite Galois group for `L/K` and `N` is a normal subgroup of `G` that is a Galois group for `L/F`, then the quotient group `G ⧸ N` is a Galois group for `F/K`. -/ instance [Finite G] [IsGaloisGroup G K L] : IsGaloisGroup (G ⧸ N) K F := letI := smulOfNormal G F L N haveI := smulDistribClass_smulOfNormal G F L N letI := mulSemiringActionOfSmulDistribClass F L G - haveI := isScalarTower_mulSemiringActionQuotient G F L N quotient G K F L N variable (E : IntermediateField K L) [hE : IsGaloisGroup H E L] @@ -746,7 +507,6 @@ theorem map_quotientMk' [Finite G] [IsGaloisGroup G K L] (h : E ≤ F) : have : SMulDistribClass G F L := smulDistribClass_smulOfNormal G F L N let := mulSemiringActionOfSmulDistribClass F L G have : IsScalarTower E F L := IsScalarTower.of_algebraMap_eq' rfl - have := isScalarTower_mulSemiringActionQuotient G F L N { faithful := have := (inferInstance : IsGaloisGroup (G ⧸ N) K F).faithful; inferInstance commutes := ⟨by intro ⟨_, g, hg, rfl⟩ x y @@ -756,7 +516,8 @@ theorem map_quotientMk' [Finite G] [IsGaloisGroup G K L] (h : E ≤ F) : isInvariant := ⟨fun x h ↦ by obtain ⟨a, ha⟩ := hE.isInvariant.isInvariant (algebraMap F L x) (by rintro ⟨g, hg⟩ - simpa only [← algebraMap.smul'] using! congr_arg (algebraMap F L) <| h ⟨g, ⟨g, hg, rfl⟩⟩) + rw [MulAction.subgroup_smul_def, ← algebraMap.smul'] + exact congr_arg (algebraMap F L) <| h ⟨g, ⟨g, hg, rfl⟩⟩) exact ⟨a, FaithfulSMul.algebraMap_injective F L (by rw [← IsScalarTower.algebraMap_apply, ha])⟩⟩ } diff --git a/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean b/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean index a1f6d27897f034..b6280f1e11761d 100644 --- a/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean +++ b/Mathlib/FieldTheory/IntermediateField/Adjoin/Basic.lean @@ -784,3 +784,8 @@ theorem AdjoinPair.algebraMap_gen₂ : (algebraMap (↥K⟮x, y⟯) L) (gen₂ K end AdjoinPair end IntermediateField + +instance (R : Type*) [CommSemiring R] (K : Type*) [Field K] [Algebra R K] + (S : Type*) [Semiring S] [Algebra R S] [Module.Finite R S] : + Finite (S →ₐ[R] K) := + .of_equiv _ (Algebra.TensorProduct.liftEquivRight _ K _ _).symm diff --git a/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean b/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean index 3f596fe43485df..99fe8c8f78d0dd 100644 --- a/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean +++ b/Mathlib/FieldTheory/IntermediateField/Adjoin/Defs.lean @@ -591,11 +591,11 @@ variable {A B C : Type*} [Field A] [Field B] [Field C] [Algebra A B] [Algebra B /-- Ring homomorphism between `A⟮b⟯` and `A⟮↑b⟯`. -/ def RingHom.adjoinAlgebraMap : A⟮b⟯ →+* A⟮((algebraMap B C) b)⟯ := RingHom.codRestrict (((Algebra.ofId B C).restrictScalars A).comp (IntermediateField.val A⟮b⟯)) _ - (fun x ↦ by - rw [show (algebraMap B C) b = (Algebra.ofId B C).restrictScalars A b by rfl, - ← Set.image_singleton, ← IntermediateField.adjoin_map A {b}] - use x - simp) + (fun x ↦ by + rw [show (algebraMap B C) b = (Algebra.ofId B C).restrictScalars A b by rfl, + ← Set.image_singleton, ← IntermediateField.adjoin_map A {b}] + use x + simp) instance : Algebra A⟮b⟯ A⟮(algebraMap B C) b⟯ := RingHom.toAlgebra (RingHom.adjoinAlgebraMap _) diff --git a/Mathlib/FieldTheory/IntermediateField/Basic.lean b/Mathlib/FieldTheory/IntermediateField/Basic.lean index ca54cf81abab2a..3e4e49af1025a5 100644 --- a/Mathlib/FieldTheory/IntermediateField/Basic.lean +++ b/Mathlib/FieldTheory/IntermediateField/Basic.lean @@ -541,24 +541,26 @@ theorem coe_fieldRange : ↑f.fieldRange = Set.range f := theorem fieldRange_toSubfield : f.fieldRange.toSubfield = (f : L →+* L').fieldRange := rfl -variable {f} - +variable {f} in @[simp] theorem mem_fieldRange {y : L'} : y ∈ f.fieldRange ↔ ∃ x, f x = y := Iff.rfl -/-- An algebra homomorphism between fields restricts to an algebra equivalence onto its range. -/ +/-- The isomorphism from `L` to the field range of the `AlgHom` `f`, sending `x` to `f x`. -/ +@[simps! apply_coe] noncomputable def equivFieldRange : L ≃ₐ[K] f.fieldRange := - AlgEquiv.ofBijective - (f.codRestrict f.range fun x ↦ mem_fieldRange.mpr ⟨x, rfl⟩) - ⟨fun _ _ h ↦ f.injective (congr_arg Subtype.val h), - fun ⟨_, hy⟩ ↦ (mem_fieldRange.mp hy).imp fun _ hx => Subtype.ext hx⟩ + .ofBijective f.rangeRestrict ⟨f.rangeRestrict.injective, fun ⟨_, ⟨x, hx⟩⟩ ↦ ⟨x, Subtype.ext hx⟩⟩ -@[simp] -theorem equivFieldRange_apply (x : L) : f.equivFieldRange x = f x := rfl +@[deprecated (since := "2026-06-20")] alias equivFieldRange_apply := equivFieldRange_apply_coe end AlgHom +variable (K L L') in +@[simp] +theorem IsScalarTower.toAlgHom_fieldRange [Algebra L L'] [IsScalarTower K L L'] : + (IsScalarTower.toAlgHom K L L').fieldRange = Set.range (algebraMap L L') := by + ext; simp + namespace IntermediateField /-- The embedding from an intermediate field of `L / K` to `L`. -/ diff --git a/Mathlib/FieldTheory/IsSepClosed.lean b/Mathlib/FieldTheory/IsSepClosed.lean index 823bf62ad7f452..fd98ad49f7d954 100644 --- a/Mathlib/FieldTheory/IsSepClosed.lean +++ b/Mathlib/FieldTheory/IsSepClosed.lean @@ -6,6 +6,7 @@ Authors: Jz Pan module public import Mathlib.FieldTheory.Galois.Basic +public import Mathlib.FieldTheory.SeparableClosure /-! # Separably Closed Field @@ -311,3 +312,43 @@ noncomputable def equiv : L ≃ₐ[K] M := (IsSepClosed.lift : L →ₐ[K] M) (IsSepClosed.lift : M →ₐ[K] L)).1 end IsSepClosure + +section separableClosure + +variable (F E : Type*) [Field F] [Field E] [Algebra F E] + +/-- If `E` is normal over `F`, then the separable closure of `F` in `E` is Galois (i.e. +normal and separable) over `F`. -/ +@[stacks 0EXK] +instance separableClosure.isGalois [Normal F E] : IsGalois F (separableClosure F E) where + to_isSeparable := separableClosure.isSeparable F E + to_normal := by + rw [← separableClosure.normalClosure_eq_self] + exact normalClosure.normal F _ E + +/-- If `E / F` is a field extension and `E` is separably closed, then the separable closure +of `F` in `E` is equal to `F` if and only if `F` is separably closed. -/ +theorem IsSepClosed.separableClosure_eq_bot_iff [IsSepClosed E] : + separableClosure F E = ⊥ ↔ IsSepClosed F := by + refine ⟨fun h ↦ IsSepClosed.of_exists_root _ fun p _ hirr hsep ↦ ?_, + fun _ ↦ IntermediateField.eq_bot_of_isSepClosed_of_isSeparable _⟩ + obtain ⟨x, hx⟩ := IsSepClosed.exists_aeval_eq_zero E p (degree_pos_of_irreducible hirr).ne' hsep + obtain ⟨x, rfl⟩ := h ▸ mem_separableClosure_iff.2 (hsep.of_dvd <| minpoly.dvd _ x hx) + exact ⟨x, by simpa [Algebra.ofId_apply] using hx⟩ + +/-- If `E` is separably closed, then the separable closure of `F` in `E` is an absolute +separable closure of `F`. -/ +instance separableClosure.isSepClosure [IsSepClosed E] : IsSepClosure F (separableClosure F E) := + ⟨(IsSepClosed.separableClosure_eq_bot_iff _ E).mp (separableClosure.separableClosure_eq_bot F E), + isSeparable F E⟩ + +/-- The absolute separable closure is defined to be the relative separable closure inside the +algebraic closure. It is indeed a separable closure (`IsSepClosure`) by +`separableClosure.isSepClosure`, and it is Galois (`IsGalois`) by `separableClosure.isGalois` +or `IsSepClosure.isGalois`, and every separable extension embeds into it (`IsSepClosed.lift`). -/ +abbrev SeparableClosure : Type _ := separableClosure F (AlgebraicClosure F) + +instance SeparableClosure.isSepClosed : IsSepClosed (SeparableClosure F) := + (inferInstance : IsSepClosure F (SeparableClosure F)).sep_closed + +end separableClosure diff --git a/Mathlib/FieldTheory/Isaacs.lean b/Mathlib/FieldTheory/Isaacs.lean index 3f214bd9523737..e29985c0cc2fe2 100644 --- a/Mathlib/FieldTheory/Isaacs.lean +++ b/Mathlib/FieldTheory/Isaacs.lean @@ -62,7 +62,7 @@ theorem nonempty_algHom_of_exists_root (h : ∀ x : E, ∃ y : K, aeval y (minpo have ⟨ω, hω⟩ := exists_algHom_adjoin_of_splits (fun s hs ↦ ⟨(alg.isIntegral).1 _, splits s hs⟩) ϕ (adjoin_simple_le_iff.mpr hα) refine ⟨ω, β, ((DFunLike.congr_fun hω <| AdjoinSimple.gen F α).trans ?_).symm⟩ - rw [AlgHom.comp_apply, AlgHom.comp_apply, AlgEquiv.coe_algHom, + rw [AlgHom.comp_apply, AlgHom.comp_apply, AlgEquiv.coe_toAlgHom, adjoinRootEquivAdjoin_symm_apply_gen, AdjoinRoot.liftAlgHom_root] rfl have ω : ∃ ω : Ω, ⊤ ≤ M ω := by diff --git a/Mathlib/FieldTheory/KummerExtension.lean b/Mathlib/FieldTheory/KummerExtension.lean index 593f21e400895f..d20b29ba748424 100644 --- a/Mathlib/FieldTheory/KummerExtension.lean +++ b/Mathlib/FieldTheory/KummerExtension.lean @@ -269,10 +269,10 @@ def autAdjoinRootXPowSubCEquiv [NeZero n] : intro e have := Fact.mk H letI : Algebra K K[n√a] := inferInstance - apply AlgEquiv.coe_algHom_injective + apply AlgEquiv.coe_toAlgHom_injective apply AdjoinRoot.algHom_ext simp only [AdjoinRootXPowSubCEquivToRootsOfUnity, AdjoinRoot.algebraMap_eq, OneHom.toFun_eq_coe, - MonoidHom.toOneHom_coe, AlgEquiv.coe_algHom, autAdjoinRootXPowSubC_root, Algebra.smul_def] + MonoidHom.toOneHom_coe, AlgEquiv.coe_toAlgHom, autAdjoinRootXPowSubC_root, Algebra.smul_def] rw [rootsOfUnityEquivOfPrimitiveRoots_symm_apply, rootsOfUnity.val_mkOfPowEq_coe] split_ifs with h · obtain rfl := not_imp_not.mp (fun hn ↦ ne_zero_of_irreducible_X_pow_sub_C' hn H) h @@ -356,7 +356,7 @@ lemma Algebra.adjoin_root_eq_top_of_isSplittingField : (adjoinRootXPowSubCEquiv hζ H hα).symm.injective rw [Algebra.map_top, (AlgHom.range_eq_top _).mpr (adjoinRootXPowSubCEquiv hζ H hα).symm.surjective, AlgHom.map_adjoin, - Set.image_singleton, AlgEquiv.coe_algHom, adjoinRootXPowSubCEquiv_symm_eq_root, + Set.image_singleton, AlgEquiv.coe_toAlgHom, adjoinRootXPowSubCEquiv_symm_eq_root, adjoinRoot_eq_top] include hζ H hα in diff --git a/Mathlib/FieldTheory/LinearDisjoint.lean b/Mathlib/FieldTheory/LinearDisjoint.lean index a9035e67cc1299..5894be8689642f 100644 --- a/Mathlib/FieldTheory/LinearDisjoint.lean +++ b/Mathlib/FieldTheory/LinearDisjoint.lean @@ -685,7 +685,7 @@ theorem isField_of_forall (A : Type v) [Field A] (B : Type w) [Field B] (AlgEquiv.ofInjective fa fa.injective) (AlgEquiv.ofInjective fb fb.injective)) := by ext <;> simp [fa, fb] replace H : Function.Injective i := by simpa only - [hi, AlgHom.coe_comp, AlgEquiv.coe_algHom, EquivLike.injective_comp, fa, this, K, fb] + [hi, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, EquivLike.injective_comp, fa, this, K, fb] change Function.Injective (Ideal.Quotient.mk M) at H rwa [RingHom.injective_iff_ker_eq_bot, Ideal.mk_ker] at H diff --git a/Mathlib/FieldTheory/Minpoly/Field.lean b/Mathlib/FieldTheory/Minpoly/Field.lean index a530c39880c1a3..f4759385159d6a 100644 --- a/Mathlib/FieldTheory/Minpoly/Field.lean +++ b/Mathlib/FieldTheory/Minpoly/Field.lean @@ -333,7 +333,7 @@ lemma minpoly_algEquiv_toLinearMap (σ : L ≃ₐ[K] L) (hσ : IsOfFinOrder σ) simp_rw [← AlgEquiv.pow_toLinearMap] at hs apply hq.ne_zero simpa using Fintype.linearIndependent_iff.mp - (((linearIndependent_algHom_toLinearMap' K L L).comp _ AlgEquiv.coe_algHom_injective).comp _ + (((linearIndependent_algHom_toLinearMap' K L L).comp _ AlgEquiv.coe_toAlgHom_injective).comp _ (Subtype.val_injective.comp ((finEquivPowers hσ).injective))) (q.coeff ∘ (↑)) hs ⟨_, H⟩ diff --git a/Mathlib/FieldTheory/PurelyInseparable/Basic.lean b/Mathlib/FieldTheory/PurelyInseparable/Basic.lean index 6baa4cd00f365a..e737a97e815e7d 100644 --- a/Mathlib/FieldTheory/PurelyInseparable/Basic.lean +++ b/Mathlib/FieldTheory/PurelyInseparable/Basic.lean @@ -6,7 +6,7 @@ Authors: Jz Pan module public import Mathlib.Algebra.CharP.IntermediateField -public import Mathlib.FieldTheory.SeparableClosure +public import Mathlib.FieldTheory.IsSepClosed /-! diff --git a/Mathlib/FieldTheory/RatFunc/AsPolynomial.lean b/Mathlib/FieldTheory/RatFunc/AsPolynomial.lean index 4b327f1accb048..cbd35a8c1442d5 100644 --- a/Mathlib/FieldTheory/RatFunc/AsPolynomial.lean +++ b/Mathlib/FieldTheory/RatFunc/AsPolynomial.lean @@ -15,6 +15,7 @@ import Mathlib.RingTheory.Valuation.IsTrivialOn /-! # Generalities on the polynomial structure of rational functions + * Main evaluation properties * Study of the X-adic valuation diff --git a/Mathlib/FieldTheory/RatFunc/Basic.lean b/Mathlib/FieldTheory/RatFunc/Basic.lean index fb732adde49cc6..d99cb6ca38a10a 100644 --- a/Mathlib/FieldTheory/RatFunc/Basic.lean +++ b/Mathlib/FieldTheory/RatFunc/Basic.lean @@ -290,10 +290,8 @@ def instAddCommGroup : AddCommGroup K⟮X⟯ where add_zero := by frac_tac neg_add_cancel := by frac_tac sub_eq_add_neg := by frac_tac - nsmul := (· • ·) nsmul_zero := by smul_tac nsmul_succ _ := by smul_tac - zsmul := (· • ·) zsmul_zero' := by smul_tac zsmul_succ' _ := by smul_tac zsmul_neg' _ := by smul_tac diff --git a/Mathlib/FieldTheory/RatFunc/IntermediateField.lean b/Mathlib/FieldTheory/RatFunc/IntermediateField.lean index c91f95b5d33eab..5fd7aeac0fe1f0 100644 --- a/Mathlib/FieldTheory/RatFunc/IntermediateField.lean +++ b/Mathlib/FieldTheory/RatFunc/IntermediateField.lean @@ -73,7 +73,7 @@ theorem minpolyX_eq_zero_iff : (f.minpolyX K⟮f⟯) = 0 ↔ ∃ c, f = C c := ⟨fun h ↦ f.eq_C_of_minpolyX_coeff_eq_zero (by simp [h]), by rintro ⟨c, rfl⟩; simp⟩ theorem isAlgebraic_adjoin_simple_X (hf : ¬∃ c, f = C c) : IsAlgebraic K⟮f⟯ (X : K⟮X⟯) := - ⟨f.minpolyX K⟮f⟯, fun H ↦ hf (f.minpolyX_eq_zero_iff.mp H), f.minpolyX_aeval_X⟩ + ⟨f.minpolyX K⟮f⟯, fun H ↦ hf (f.minpolyX_eq_zero_iff.mp H), f.minpolyX_aeval_X⟩ theorem isAlgebraic_adjoin_simple_X' (hf : ¬∃ c, f = C c) : Algebra.IsAlgebraic K⟮f⟯ K⟮X⟯ := by diff --git a/Mathlib/FieldTheory/Separable.lean b/Mathlib/FieldTheory/Separable.lean index 3d7f1ab88bce21..6fe3e6458fa074 100644 --- a/Mathlib/FieldTheory/Separable.lean +++ b/Mathlib/FieldTheory/Separable.lean @@ -755,6 +755,13 @@ lemma Algebra.IsSeparable.of_equiv_equiv [Algebra.IsSeparable A₁ B₁] : Algeb ⟨fun x ↦ (e₂.apply_symm_apply x) ▸ _root_.IsSeparable.of_equiv_equiv e₁ e₂ he (Algebra.IsSeparable.isSeparable _ _)⟩ +lemma Algebra.IsSeparable.iff_of_equiv_equiv : + Algebra.IsSeparable A₁ B₁ ↔ Algebra.IsSeparable A₂ B₂ := + ⟨fun _ ↦ Algebra.IsSeparable.of_equiv_equiv e₁ e₂ he, + fun _ ↦ Algebra.IsSeparable.of_equiv_equiv e₁.symm e₂.symm (by + ext x + simpa [RingEquiv.eq_symm_apply] using (RingHom.ext_iff.mp he (e₁.symm x)).symm)⟩ + end AlgEquiv section CardAlgHom diff --git a/Mathlib/FieldTheory/SeparableClosure.lean b/Mathlib/FieldTheory/SeparableClosure.lean index b6aafbad0637f8..43518d5675b070 100644 --- a/Mathlib/FieldTheory/SeparableClosure.lean +++ b/Mathlib/FieldTheory/SeparableClosure.lean @@ -6,7 +6,6 @@ Authors: Jz Pan module public import Mathlib.FieldTheory.SeparableDegree -public import Mathlib.FieldTheory.IsSepClosed public import Mathlib.RingTheory.AlgebraicIndependent.AlgebraicClosure /-! @@ -64,6 +63,8 @@ separable degree, degree, separable closure @[expose] public section +assert_not_exists IsGalois + open Module Polynomial IntermediateField Field noncomputable section @@ -180,46 +181,20 @@ theorem separableClosure.normalClosure_eq_self : (AlgEquiv.Algebra.isSeparable (AlgEquiv.ofInjectiveField i)) le_separableClosure F E _) (le_normalClosure _) -/-- If `E` is normal over `F`, then the separable closure of `F` in `E` is Galois (i.e. -normal and separable) over `F`. -/ -@[stacks 0EXK] -instance separableClosure.isGalois [Normal F E] : IsGalois F (separableClosure F E) where - to_isSeparable := separableClosure.isSeparable F E - to_normal := by - rw [← separableClosure.normalClosure_eq_self] - exact normalClosure.normal F _ E - -/-- If `E / F` is a field extension and `E` is separably closed, then the separable closure -of `F` in `E` is equal to `F` if and only if `F` is separably closed. -/ -theorem IsSepClosed.separableClosure_eq_bot_iff [IsSepClosed E] : - separableClosure F E = ⊥ ↔ IsSepClosed F := by - refine ⟨fun h ↦ IsSepClosed.of_exists_root _ fun p _ hirr hsep ↦ ?_, - fun _ ↦ IntermediateField.eq_bot_of_isSepClosed_of_isSeparable _⟩ - obtain ⟨x, hx⟩ := IsSepClosed.exists_aeval_eq_zero E p (degree_pos_of_irreducible hirr).ne' hsep - obtain ⟨x, rfl⟩ := h ▸ mem_separableClosure_iff.2 (hsep.of_dvd <| minpoly.dvd _ x hx) - exact ⟨x, by simpa [Algebra.ofId_apply] using hx⟩ - -/-- If `E` is separably closed, then the separable closure of `F` in `E` is an absolute -separable closure of `F`. -/ -instance separableClosure.isSepClosure [IsSepClosed E] : IsSepClosure F (separableClosure F E) := - ⟨(IsSepClosed.separableClosure_eq_bot_iff _ E).mp (separableClosure.separableClosure_eq_bot F E), - isSeparable F E⟩ - -/-- The absolute separable closure is defined to be the relative separable closure inside the -algebraic closure. It is indeed a separable closure (`IsSepClosure`) by -`separableClosure.isSepClosure`, and it is Galois (`IsGalois`) by `separableClosure.isGalois` -or `IsSepClosure.isGalois`, and every separable extension embeds into it (`IsSepClosed.lift`). -/ -abbrev SeparableClosure : Type _ := separableClosure F (AlgebraicClosure F) - -instance SeparableClosure.isSepClosed : IsSepClosed (SeparableClosure F) := - (inferInstance : IsSepClosure F (SeparableClosure F)).sep_closed - /-- `F(S) / F` is a separable extension if and only if all elements of `S` are separable elements. -/ theorem IntermediateField.isSeparable_adjoin_iff_isSeparable {S : Set E} : Algebra.IsSeparable F (adjoin F S) ↔ ∀ x ∈ S, IsSeparable F x := (le_separableClosure_iff F E _).symm.trans adjoin_le_iff +/-- If `p` is a separable polynomial with splitting field `E` over `F`, then `E / F` is a +separable extension. -/ +theorem Algebra.isSeparable_of_separable_splitting_field {p : F[X]} + [sp : p.IsSplittingField F E] (hp : p.Separable) : Algebra.IsSeparable F E := by + rw [← isSeparable_top, ← (isSplittingField_iff_intermediateField.mp sp).2, + isSeparable_adjoin_iff_isSeparable] + exact fun x hx ↦ hp.of_dvd (minpoly.dvd F x (aeval_eq_zero_of_mem_rootSet hx)) + /-- The separable closure of `F` in `E` is equal to `E` if and only if `E / F` is separable. -/ theorem separableClosure.eq_top_iff : separableClosure F E = ⊤ ↔ Algebra.IsSeparable F E := @@ -284,7 +259,7 @@ lemma separableClosure_le_separableClosure_iff [Algebra K E] [IsScalarTower F K E] {L : IntermediateField F E} : (separableClosure L E).restrictScalars F ≤ (separableClosure K E).restrictScalars F ↔ L ≤ (separableClosure K E).restrictScalars F := - (isClosed_restrictScalars_separableClosure F E K).closure_le_iff + (isClosed_restrictScalars_separableClosure F E K).closure_le_iff end separableClosure diff --git a/Mathlib/FieldTheory/SeparableDegree.lean b/Mathlib/FieldTheory/SeparableDegree.lean index 5b90913ccb2b6f..cd80f415c6cef7 100644 --- a/Mathlib/FieldTheory/SeparableDegree.lean +++ b/Mathlib/FieldTheory/SeparableDegree.lean @@ -159,7 +159,7 @@ def embEquivOfEquiv (i : E ≃ₐ[F] K) : intro x have h := isAlgebraic_algebraMap (R := E) (A := K) (i.symm.toAlgHom x) rw [show ∀ y : E, (algebraMap E K) y = i.toAlgHom y from fun y ↦ rfl] at h - simpa only [AlgEquiv.coe_algHom, AlgEquiv.apply_symm_apply] using h + simpa only [AlgEquiv.coe_toAlgHom, AlgEquiv.apply_symm_apply] using h apply AlgEquiv.restrictScalars (R := F) (S := E) exact IsAlgClosure.equivOfAlgebraic E K (AlgebraicClosure K) (AlgebraicClosure E) @@ -196,6 +196,13 @@ theorem finSepDegree_top : finSepDegree F (⊤ : IntermediateField E K) = finSep end Tower +theorem isSeparable_bot : Algebra.IsSeparable F (⊥ : IntermediateField F E) := + AlgEquiv.Algebra.isSeparable (IntermediateField.botEquiv F E).symm + +theorem isSeparable_top : + Algebra.IsSeparable F (⊤ : IntermediateField F E) ↔ Algebra.IsSeparable F E := + Algebra.IsSeparable.iff_of_equiv_equiv (RingEquiv.refl F) topEquiv.toRingEquiv (by ext; simp) + end IntermediateField namespace Field diff --git a/Mathlib/FieldTheory/SeparablyGenerated.lean b/Mathlib/FieldTheory/SeparablyGenerated.lean index 5301cf752be973..64e81115ac3599 100644 --- a/Mathlib/FieldTheory/SeparablyGenerated.lean +++ b/Mathlib/FieldTheory/SeparablyGenerated.lean @@ -61,22 +61,24 @@ theorem aeval_toPolynomialAdjoinImageCompl_eq_zero {a : ι → K} {F : MvPolynomial ι k} (hFa : F.aeval a = 0) (i : ι) : (toPolynomialAdjoinImageCompl F a i).aeval (a i) = 0 := by rw [← hFa, ← AlgHom.restrictScalars_apply k] - simp_rw [toPolynomialAdjoinImageCompl, ← AlgEquiv.coe_algHom, ← AlgHom.comp_apply] + simp_rw [toPolynomialAdjoinImageCompl, ← AlgEquiv.coe_toAlgHom, ← AlgHom.comp_apply] congr; ext; aesop (add simp optionEquivLeft_X_some) (add simp optionEquivLeft_X_none) -set_option backward.isDefEq.respectTransparency false in theorem irreducible_toPolynomialAdjoinImageCompl {F : MvPolynomial ι k} (hF : Irreducible F) (i : ι) (H : AlgebraicIndependent k fun x : {j | j ≠ i} ↦ a x) : Irreducible (toPolynomialAdjoinImageCompl F a i) := by - have : a '' {i}ᶜ = Set.range (fun x : {j | j ≠ i} ↦ a x) := by ext; simp - delta toPolynomialAdjoinImageCompl - convert! - hF.map (renameEquiv k (Equiv.optionSubtypeNe i).symm) |>.map (optionEquivLeft k _) |>.map - (Polynomial.mapAlgEquiv - (H.aevalEquiv.trans (Subalgebra.equivOfEq _ _ congr(Algebra.adjoin k $this.symm)))) - rw [← AlgEquiv.coe_algHom] - congr - aesop + classical + unfold toPolynomialAdjoinImageCompl + have hc : a '' {i}ᶜ = Set.range (fun x : {j | j ≠ i} ↦ a x) := by ext; simp + let d : {j // j ≠ i} ≃ {j | j ≠ i} := .subtypeEquivRight (by simp) + refine (congrArg Irreducible ?_).mp <| + hF.map (renameEquiv k ((Equiv.optionSubtypeNe i).symm)) |>.map + (optionEquivLeft k _) |>.map (Polynomial.mapAlgEquiv <| + (renameEquiv k d).trans <| H.aevalEquiv.trans + (Subalgebra.equivOfEq _ _ congr(Algebra.adjoin k $hc.symm))) + rw [Polynomial.coe_mapAlgEquiv, Polynomial.coe_mapAlgHom] + refine congrFun (congrArg Polynomial.map ?_) _ + ext <;> simp [d] -- Suppose `F` has minimal total degree among the relations of `a`. variable {F : MvPolynomial ι k} diff --git a/Mathlib/Geometry/Convex/Cone/Basic.lean b/Mathlib/Geometry/Convex/Cone/Basic.lean index 835f9e94b4093c..71b6c33e5bdce0 100644 --- a/Mathlib/Geometry/Convex/Cone/Basic.lean +++ b/Mathlib/Geometry/Convex/Cone/Basic.lean @@ -6,6 +6,7 @@ Authors: Yury Kudryashov, Frédéric Dupuis module public import Mathlib.Analysis.Convex.Hull +public import Mathlib.Tactic.CrossRefAttribute /-! # Convex cones @@ -55,6 +56,7 @@ variable [Semiring R] [PartialOrder R] variable (R M) in /-- A convex cone is a subset `s` of an `R`-module such that `a • x + b • y ∈ s` whenever `a, b > 0` and `x, y ∈ s`. -/ +@[wikidata Q2256541] structure ConvexCone [AddCommMonoid M] [SMul R M] where /-- The **carrier set** underlying this cone: the set of points contained in it -/ carrier : Set M diff --git a/Mathlib/Geometry/Convex/Cone/Pointed.lean b/Mathlib/Geometry/Convex/Cone/Pointed.lean index 9ce1c7fa9c4d0b..f2370255b2ba01 100644 --- a/Mathlib/Geometry/Convex/Cone/Pointed.lean +++ b/Mathlib/Geometry/Convex/Cone/Pointed.lean @@ -6,6 +6,7 @@ Authors: Apurva Nakade module public import Mathlib.Algebra.Group.Submonoid.Support +public import Mathlib.Algebra.Order.Monoid.Submonoid public import Mathlib.Algebra.Order.Nonneg.Module public import Mathlib.Geometry.Convex.Cone.Basic @@ -128,6 +129,11 @@ lemma convex (C : PointedCone R E) : Convex R (C : Set E) := C.toConvexCone.conv nonrec lemma smul_mem (C : PointedCone R E) (hr : 0 ≤ r) (hx : x ∈ C) : r • x ∈ C := C.smul_mem ⟨r, hr⟩ hx +lemma smul_mem_iff {𝕜 M : Type*} [Field 𝕜] [LinearOrder 𝕜] [IsStrictOrderedRing 𝕜] + [AddCommMonoid M] [Module 𝕜 M] (C : PointedCone 𝕜 M) + {c : 𝕜} (hc : 0 < c) {x : M} : c • x ∈ C ↔ x ∈ C := + ⟨fun h => inv_smul_smul₀ hc.ne' x ▸ C.smul_mem (inv_pos.2 hc).le h, C.smul_mem hc.le⟩ + /-- The `PointedCone` constructed from a pointed `ConvexCone`. -/ def _root_.ConvexCone.toPointedCone (C : ConvexCone R E) (hC : C.Pointed) : PointedCone R E where carrier := C @@ -278,8 +284,10 @@ variable [AddCommMonoid E] [PartialOrder E] [IsOrderedAddMonoid E] [Module R E] /-- The positive cone is the pointed cone formed by the set of nonnegative elements in an ordered module. -/ -def positive : PointedCone R E := - (ConvexCone.positive R E).toPointedCone ConvexCone.pointed_positive +@[simps!] +def positive : PointedCone R E where + __ := AddSubmonoid.nonneg E + smul_mem' c _ hx := by simpa using smul_nonneg c.property hx @[simp] theorem mem_positive {x : E} : x ∈ positive R E ↔ 0 ≤ x := diff --git a/Mathlib/Geometry/Convex/ConvexSpace/Module.lean b/Mathlib/Geometry/Convex/ConvexSpace/Module.lean index 155ef732f0bc2e..bfd6a3e1498226 100644 --- a/Mathlib/Geometry/Convex/ConvexSpace/Module.lean +++ b/Mathlib/Geometry/Convex/ConvexSpace/Module.lean @@ -83,7 +83,7 @@ lemma convexCombPair_eq_sum (a b : R) (ha hb hab) (x y : M) : classical simp [convexCombPair, sConvexComb_eq_sum, Finsupp.sum_add_index, add_smul] lemma IsAffineMap.map_sum_weights (hf : IsAffineMap R f) (w : StdSimplex R I) (g : I → M) : - f (w.weights.sum fun i r ↦ r • g i) = w.weights.sum fun i r ↦ r • f (g i) := by + f (w.weights.sum fun i r ↦ r • g i) = w.weights.sum fun i r ↦ r • f (g i) := by simpa using hf.map_iConvexComb w g lemma IsAffineMap.map_smul_add_smul (hf : IsAffineMap R f) (ha : 0 ≤ a) (hb : 0 ≤ b) diff --git a/Mathlib/Geometry/Euclidean/Altitude.lean b/Mathlib/Geometry/Euclidean/Altitude.lean index 1d100ab3c1c959..cf2c704ce1e916 100644 --- a/Mathlib/Geometry/Euclidean/Altitude.lean +++ b/Mathlib/Geometry/Euclidean/Altitude.lean @@ -260,10 +260,10 @@ lemma height_pos {n : ℕ} [NeZero n] (s : Simplex ℝ P n) (i : Fin (n + 1)) : open Qq Mathlib.Meta.Positivity in /-- Extension for the `positivity` tactic: the height of a simplex is always positive. -/ @[positivity height _ _] -meta def evalHeight : PositivityExt where eval {u α} _ pα? e := do +meta def evalHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@height $V $P $i1 $i2 $i3 $i4 $n $hn $s $i) => - let some _ := pα? | pure .none assertInstancesCommute return .positive q(height_pos $s $i) | _, _, _ => throwError "not Simplex.height" diff --git a/Mathlib/Geometry/Euclidean/Angle/Unoriented/Affine.lean b/Mathlib/Geometry/Euclidean/Angle/Unoriented/Affine.lean index 45bf2de5808eef..f583bf76b20867 100644 --- a/Mathlib/Geometry/Euclidean/Angle/Unoriented/Affine.lean +++ b/Mathlib/Geometry/Euclidean/Angle/Unoriented/Affine.lean @@ -117,6 +117,18 @@ theorem angle_const_sub (v : V) (v₁ v₂ v₃ : V) : ∠ (v - v₁) (v - v₂) theorem angle_neg (v₁ v₂ v₃ : V) : ∠ (-v₁) (-v₂) (-v₃) = ∠ v₁ v₂ v₃ := by simpa only [zero_sub] using angle_const_sub 0 v₁ v₂ v₃ +theorem angle_smul_right_of_pos (p₁ : P) {p₂ p₃ p₄ : P} {r : ℝ} (hr : 0 < r) + (hrv : r • (p₄ -ᵥ p₂) = p₃ -ᵥ p₂) : + ∠ p₁ p₂ p₃ = ∠ p₁ p₂ p₄ := by + simp only [angle, ← hrv] + exact InnerProductGeometry.angle_smul_right_of_pos (p₁ -ᵥ p₂) (p₄ -ᵥ p₂) hr + +theorem angle_smul_left_of_pos {p₁ p₂ p₄ : P} (p₃ : P) {r : ℝ} (hr : 0 < r) + (hrv : r • (p₄ -ᵥ p₂) = p₁ -ᵥ p₂) : + ∠ p₁ p₂ p₃ = ∠ p₄ p₂ p₃ := by + simp only [angle, ← hrv] + exact InnerProductGeometry.angle_smul_left_of_pos (p₄ -ᵥ p₂) (p₃ -ᵥ p₂) hr + /-- The angle at a point does not depend on the order of the other two points. -/ nonrec theorem angle_comm (p₁ p₂ p₃ : P) : ∠ p₁ p₂ p₃ = ∠ p₃ p₂ p₁ := @@ -167,9 +179,11 @@ theorem angle_eq_angle_of_angle_eq_pi (p₁ : P) {p₂ p₃ p₄ : P} (h : ∠ p unfold angle at * rcases angle_eq_pi_iff.1 h with ⟨_, ⟨r, ⟨hr, hpr⟩⟩⟩ rw [eq_comm] - convert! angle_smul_right_of_pos (p₁ -ᵥ p₂) (p₃ -ᵥ p₂) (add_pos (neg_pos_of_neg hr) zero_lt_one) - rw [add_smul, ← neg_vsub_eq_vsub_rev p₂ p₃, smul_neg, neg_smul, ← hpr] - simp + replace hpr : (-r + 1) • (p₃ -ᵥ p₂) = p₄ -ᵥ p₂ := by + rw [add_smul, ← neg_vsub_eq_vsub_rev p₂ p₃, smul_neg, neg_smul, ← hpr] + simp + replace hr : 0 < -r + 1 := by linarith + exact angle_smul_right_of_pos p₁ hr hpr /-- If ∠BCD = π, then ∠ACB + ∠ACD = π. -/ nonrec theorem angle_add_angle_eq_pi_of_angle_eq_pi (p₁ : P) {p₂ p₃ p₄ : P} (h : ∠ p₂ p₃ p₄ = π) : @@ -227,11 +241,9 @@ theorem dist_eq_abs_sub_dist_iff_angle_eq_zero {p₁ p₂ p₃ : P} (hp₁p₂ : /-- If M is the midpoint of the segment AB, then ∠AMB = π. -/ theorem angle_midpoint_eq_pi (p₁ p₂ : P) (hp₁p₂ : p₁ ≠ p₂) : ∠ p₁ (midpoint ℝ p₁ p₂) p₂ = π := by - simp only [angle, left_vsub_midpoint, invOf_eq_inv, right_vsub_midpoint, inv_pos, zero_lt_two, - angle_smul_right_of_pos, angle_smul_left_of_pos] - rw [← neg_vsub_eq_vsub_rev p₁ p₂] - apply angle_self_neg_of_nonzero - simpa only [ne_eq, vsub_eq_zero_iff_eq] + suffices dist p₁ p₂ = dist p₁ (midpoint ℝ p₁ p₂) + dist (midpoint ℝ p₁ p₂) p₂ by + rwa [← dist_eq_add_dist_iff_angle_eq_pi (by simpa) (by simpa), dist_comm p₂] + simp [dist_eq_norm_vsub V, left_vsub_midpoint, midpoint_vsub_right, norm_smul, ← two_mul] /-- If M is the midpoint of the segment AB and C is the same distance from A as it is from B then ∠CMA = π / 2. -/ diff --git a/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean b/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean index c059c3844d66a2..e0887fd418d0f1 100644 --- a/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean +++ b/Mathlib/Geometry/Euclidean/Angle/Unoriented/RightAngle.lean @@ -328,8 +328,8 @@ variable {V : Type*} {P : Type*} [NormedAddCommGroup V] [InnerProductSpace ℝ V theorem dist_sq_eq_dist_sq_add_dist_sq_iff_angle_eq_pi_div_two (p₁ p₂ p₃ : P) : dist p₁ p₃ * dist p₁ p₃ = dist p₁ p₂ * dist p₁ p₂ + dist p₃ p₂ * dist p₃ p₂ ↔ ∠ p₁ p₂ p₃ = π / 2 := by - erw [dist_comm p₃ p₂, dist_eq_norm_vsub V p₁ p₃, dist_eq_norm_vsub V p₁ p₂, - dist_eq_norm_vsub V p₂ p₃, ← norm_sub_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two, + rw [dist_comm p₃ p₂, dist_eq_norm_vsub V p₁ p₃, dist_eq_norm_vsub V p₁ p₂, + dist_eq_norm_vsub V p₂ p₃, angle, ← norm_sub_sq_eq_norm_sq_add_norm_sq_iff_angle_eq_pi_div_two, vsub_sub_vsub_cancel_right p₁, ← neg_vsub_eq_vsub_rev p₂ p₃, norm_neg] /-- An angle in a right-angled triangle expressed using `arccos`. -/ diff --git a/Mathlib/Geometry/Euclidean/Sphere/Basic.lean b/Mathlib/Geometry/Euclidean/Sphere/Basic.lean index e641855739337f..89c2f614e760ed 100644 --- a/Mathlib/Geometry/Euclidean/Sphere/Basic.lean +++ b/Mathlib/Geometry/Euclidean/Sphere/Basic.lean @@ -138,6 +138,11 @@ lemma Sphere.radius_nonneg_of_mem {s : Sphere P} {p : P} (h : p ∈ s) : 0 ≤ s @[simp] lemma Sphere.center_mem_iff {s : Sphere P} : s.center ∈ s ↔ s.radius = 0 := by simp [mem_sphere, eq_comm] +/-- A point of a sphere that differs from another point of the sphere is not its center. -/ +lemma Sphere.ne_center_of_mem_of_mem_of_ne {s : Sphere P} {p q : P} + (hp : p ∈ s) (hq : q ∈ s) (hpq : p ≠ q) : p ≠ s.center := by + grind [dist_eq_zero, mem_sphere'] + /-- A set of points is cospherical if they are equidistant from some point. In two dimensions, this is the same thing as being concyclic. -/ @@ -206,6 +211,12 @@ theorem Cospherical.subtype_val {S : AffineSubspace ℝ P} [Nonempty S] {ps : Se (hps : Cospherical ps) : Cospherical (Subtype.val '' ps) := Isometry.cospherical S.subtypeₐᵢ.isometry hps +omit [NormedSpace ℝ V] in +/-- For a point on a sphere, the norm of its displacement from the center equals the radius. -/ +theorem norm_vsub_center_eq_radius {s : Sphere P} {p : P} (hp : p ∈ s) : + ‖p -ᵥ s.center‖ = s.radius := by + rw [← dist_eq_norm_vsub']; exact mem_sphere'.mp hp + lemma Sphere.nonempty_iff [Nontrivial V] {s : Sphere P} : (s : Set P).Nonempty ↔ 0 ≤ s.radius := by refine ⟨fun ⟨p, hp⟩ ↦ radius_nonneg_of_mem hp, fun h ↦ ?_⟩ obtain ⟨v, hv⟩ := (NormedSpace.sphere_nonempty (x := (0 : V)) (r := s.radius)).2 h @@ -456,8 +467,8 @@ theorem Sphere.dist_center_lt_radius_of_sbtw {p₁ p₂ p : P} {s : Sphere P} have ht₁' : t < 1 := lt_of_le_of_ne ht₁ fun h => hne₂ <| by rw [← hpt, h, AffineMap.lineMap_apply_one] set u := p₁ -ᵥ o; set v := p₂ -ᵥ o - have hu : ‖u‖ = s.radius := by rw [← dist_eq_norm_vsub]; exact mem_sphere.mp hp₁ - have hv : ‖v‖ = s.radius := by rw [← dist_eq_norm_vsub]; exact mem_sphere.mp hp₂ + have hu : ‖u‖ = s.radius := norm_vsub_center_eq_radius hp₁ + have hv : ‖v‖ = s.radius := norm_vsub_center_eq_radius hp₂ have huv : u ≠ v := fun h => hne₁ <| by rw [← hpt, vsub_left_cancel h, AffineMap.lineMap_same, AffineMap.const_apply] have hpo : p -ᵥ o = (1 - t) • u + t • v := by @@ -551,8 +562,8 @@ the radius vector at one endpoint is negative. -/ theorem inner_vsub_center_vsub_pos {p₁ p₂ : P} {s : Sphere P} (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) (hp₁p₂ : p₁ ≠ p₂) : 0 < ⟪p₂ -ᵥ p₁, s.center -ᵥ p₁⟫ := by - have hp₁' : ‖p₁ -ᵥ s.center‖ = s.radius := by rw [← dist_eq_norm_vsub']; exact mem_sphere'.mp hp₁ - have hp₂' : ‖p₂ -ᵥ s.center‖ = s.radius := by rw [← dist_eq_norm_vsub']; exact mem_sphere'.mp hp₂ + have hp₁' : ‖p₁ -ᵥ s.center‖ = s.radius := norm_vsub_center_eq_radius hp₁ + have hp₂' : ‖p₂ -ᵥ s.center‖ = s.radius := norm_vsub_center_eq_radius hp₂ have hd : ‖p₂ -ᵥ s.center‖ ^ 2 = ‖p₂ -ᵥ p₁‖ ^ 2 + 2 * ⟪p₂ -ᵥ p₁, p₁ -ᵥ s.center⟫ + ‖p₁ -ᵥ s.center‖ ^ 2 := by rw [← vsub_add_vsub_cancel p₂ p₁ s.center, norm_add_sq_real] @@ -593,6 +604,17 @@ lemma isDiameter_iff_mem_and_mem_and_wbtw : rw [mem_sphere.1 h₁, mem_sphere'.1 h₂, ← two_mul, eq_comm] at hd exact isDiameter_iff_mem_and_mem_and_dist.2 ⟨h₁, h₂, hd⟩ +/-- The center lies on the line through two distinct points of a sphere if and only if those +points are the endpoints of a diameter. -/ +theorem center_mem_affineSpan_pair_iff_isDiameter + (hp₁ : p₁ ∈ s) (hp₂ : p₂ ∈ s) (hp₁p₂ : p₁ ≠ p₂) : + s.center ∈ line[ℝ, p₁, p₂] ↔ s.IsDiameter p₁ p₂ := by + rw [isDiameter_iff_mem_and_mem_and_wbtw] + refine ⟨fun h => ⟨hp₁, hp₂, ?_⟩, fun h => h.2.2.mem_affineSpan⟩ + refine wbtw_of_collinear_of_dist_center_le_radius ?_ hp₁ ?_ hp₂ hp₁p₂ + · rw [Set.insert_comm]; exact collinear_insert_of_mem_affineSpan_pair h + · simpa using radius_nonneg_of_mem hp₁ + end Sphere end EuclideanSpace diff --git a/Mathlib/Geometry/Euclidean/Triangle.lean b/Mathlib/Geometry/Euclidean/Triangle.lean index f8e9b497040c23..30fe0c9dafd0b5 100644 --- a/Mathlib/Geometry/Euclidean/Triangle.lean +++ b/Mathlib/Geometry/Euclidean/Triangle.lean @@ -357,6 +357,31 @@ lemma angle_add_of_ne_of_ne {a b c p : P} (hb : a ≠ b) (hc : a ≠ c) (hp : Wb have ep := angle_add_angle_eq_pi_of_angle_eq_pi a hp linarith only [ea, eb, ec, ep] +/-- If `X` lies strictly between `A` and `C`, then `∠ A P X + ∠ X P C = ∠ A P C`, +with no nondegeneracy assumption on the point `P`. -/ +lemma angle_add_angle_eq_of_sbtw {a c p x : P} (hx : Sbtw ℝ a x c) : + ∠ a p x + ∠ x p c = ∠ a p c := by + rcases eq_or_ne p a with rfl | hpa + · simp [(hx.angle_eq_right x).symm.trans (angle_self_of_ne hx.ne_left)] + rcases eq_or_ne p c with rfl | hpc + · simp [(hx.symm.angle_eq_right a).trans (angle_self_of_ne hx.left_ne_right)] + exact angle_add_of_ne_of_ne hpa hpc hx.wbtw + +/-- If `B` lies on the same ray from `P` as a point `X` strictly between `A` and `C`, +then `∠ A P B + ∠ B P C = ∠ A P C`. -/ +theorem angle_add_angle_eq_of_sbtw_of_sameRay {a b c p x : P} + (hx : Sbtw ℝ a x c) (hxb : SameRay ℝ (x -ᵥ p) (b -ᵥ p)) (hb : b ≠ p) : + ∠ a p b + ∠ b p c = ∠ a p c := by + rcases eq_or_ne p x with rfl | hpx + · have hpi : ∠ a p c = π := hx.angle₁₂₃_eq_pi + rw [hpi, angle_comm a p b] + exact angle_add_angle_eq_pi_of_angle_eq_pi b hpi + obtain ⟨r, hr, hrb⟩ := (exists_pos_left_iff_sameRay (by aesop) (by aesop)).2 hxb + have hab : ∠ a p b = ∠ a p x := angle_smul_right_of_pos a hr hrb + have hbc : ∠ b p c = ∠ x p c := angle_smul_left_of_pos c hr hrb + rw [hab, hbc] + exact angle_add_angle_eq_of_sbtw hx + /-- **Stewart's Theorem**. -/ theorem dist_sq_mul_dist_add_dist_sq_mul_dist (a b c p : P) (h : ∠ b p c = π) : dist a b ^ 2 * dist c p + dist a c ^ 2 * dist b p = diff --git a/Mathlib/Geometry/Manifold/Algebra/Monoid.lean b/Mathlib/Geometry/Manifold/Algebra/Monoid.lean index fd7802b4573944..46a6e8ae92fe01 100644 --- a/Mathlib/Geometry/Manifold/Algebra/Monoid.lean +++ b/Mathlib/Geometry/Manifold/Algebra/Monoid.lean @@ -11,6 +11,7 @@ public import Mathlib.Geometry.Manifold.MFDeriv.Basic /-! # `C^n` monoid + A `C^n` monoid is a monoid that is also a `C^n` manifold, in which multiplication is a `C^n` map of the product manifold `G` × `G` into `G`. diff --git a/Mathlib/Geometry/Manifold/Algebra/SMul.lean b/Mathlib/Geometry/Manifold/Algebra/SMul.lean index b9680bc87af096..0f2d1913bd6bb9 100644 --- a/Mathlib/Geometry/Manifold/Algebra/SMul.lean +++ b/Mathlib/Geometry/Manifold/Algebra/SMul.lean @@ -173,8 +173,7 @@ instance {n : ℕ∞ω} : ContMDiffSMul 𝓘(𝕜) 𝓘(𝕜, E) n 𝕜 E where exact contDiff_smul.contMDiff.comp h /-- The monoid `E →L[𝕜] E` of continuous linear endomorphisms of `E` acts smoothly on `E`. -/ -instance [CompleteSpace E] {n : ℕ∞ω} : - ContMDiffSMul 𝓘(𝕜, E →L[𝕜] E) 𝓘(𝕜, E) n (E →L[𝕜] E) E where +instance {n : ℕ∞ω} : ContMDiffSMul 𝓘(𝕜, E →L[𝕜] E) 𝓘(𝕜, E) n (E →L[𝕜] E) E where contMDiff_smul := by have h : ContMDiff (𝓘(𝕜, E →L[𝕜] E).prod 𝓘(𝕜, E)) 𝓘(𝕜, (E →L[𝕜] E) × E) n (@id ((E →L[𝕜] E) × E)) := by diff --git a/Mathlib/Geometry/Manifold/ChartedSpace.lean b/Mathlib/Geometry/Manifold/ChartedSpace.lean index c5f1f48b6ac267..778649d7c467bc 100644 --- a/Mathlib/Geometry/Manifold/ChartedSpace.lean +++ b/Mathlib/Geometry/Manifold/ChartedSpace.lean @@ -6,7 +6,7 @@ Authors: Sébastien Gouëzel module public import Mathlib.Geometry.Manifold.StructureGroupoid -public import Mathlib.Topology.Connected.LocPathConnected +public import Mathlib.Topology.Connected.LocallyPathConnected public import Mathlib.Topology.IsLocalHomeomorph public import Mathlib.Topology.OpenPartialHomeomorph.Constructions @@ -265,7 +265,8 @@ theorem ChartedSpace.locallyConnectedSpace [LocallyConnectedSpace H] : LocallyCo /-- If a topological space `M` admits an atlas with locally path-connected charts, then `M` itself is locally path-connected. -/ -theorem ChartedSpace.locPathConnectedSpace [LocPathConnectedSpace H] : LocPathConnectedSpace M := by +theorem ChartedSpace.locallyPathConnectedSpace [LocallyPathConnectedSpace H] : + LocallyPathConnectedSpace M := by refine ⟨fun x ↦ ⟨fun s ↦ ⟨fun hs ↦ ?_, fun ⟨u, hu⟩ ↦ Filter.mem_of_superset hu.1.1 hu.2⟩⟩⟩ let e := chartAt H x let t := s ∩ e.source @@ -280,6 +281,9 @@ theorem ChartedSpace.locPathConnectedSpace [LocPathConnectedSpace H] : LocPathCo · exact (image_mono pathComponentIn_subset).trans (PartialEquiv.symm_image_image_of_subset_source _ inter_subset_right).subset +@[deprecated (since := "2026-06-21")] +alias ChartedSpace.locPathConnectedSpace := ChartedSpace.locallyPathConnectedSpace + /-- If `M` is modelled on `H'` and `H'` is itself modelled on `H`, then we can consider `M` as being modelled on `H`. -/ @[implicit_reducible] @@ -591,6 +595,12 @@ def IsLocalHomeomorph.chartedSpace ChartedSpace H M' := hf.chartedSpaceOfRightInverse hf'.hasRightInverse.choose_spec +/-- Given a homeomorphism `f : M ≃ₜ M'`, endow `M'` with a `ChartedSpace` structure by pushing +forward the `ChartedSpace` structure from `M`. -/ +@[implicit_reducible] +def Homeomorph.chartedSpace (f : M ≃ₜ M') : ChartedSpace H M' := + f.isLocalHomeomorph.chartedSpace f.surjective + end IsLocalHomeomorph end Constructions diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean b/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean index 78a63c9fcaf56b..811c6456c64ac7 100644 --- a/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean +++ b/Mathlib/Geometry/Manifold/ContMDiff/Atlas.lean @@ -88,17 +88,18 @@ theorem contMDiffOn_chart_symm [IsManifold I n M] : ContMDiffOn I I n (chartAt H x).symm (chartAt H x).target := contMDiffOn_symm_of_mem_maximalAtlas <| chart_mem_maximalAtlas x -theorem contMDiffAt_extend {x : M} (he : e ∈ maximalAtlas I n M) (hx : x ∈ e.source) : +theorem OpenPartialHomeomorph.contMDiffAt_extend {x : M} + (he : e ∈ maximalAtlas I n M) (hx : x ∈ e.source) : ContMDiffAt I 𝓘(𝕜, E) n (e.extend I) x := (I.contMDiff _).comp x <| contMDiffAt_of_mem_maximalAtlas he hx -theorem contMDiffOn_extend (he : e ∈ maximalAtlas I n M) : +theorem OpenPartialHomeomorph.contMDiffOn_extend (he : e ∈ maximalAtlas I n M) : ContMDiffOn I 𝓘(𝕜, E) n (e.extend I) e.source := - fun _x' hx' ↦ (contMDiffAt_extend he hx').contMDiffWithinAt + fun _x' hx' ↦ (e.contMDiffAt_extend he hx').contMDiffWithinAt theorem contMDiffAt_extChartAt' [IsManifold I n M] {x' : M} (h : x' ∈ (chartAt H x).source) : ContMDiffAt I 𝓘(𝕜, E) n (extChartAt I x) x' := - contMDiffAt_extend (chart_mem_maximalAtlas x) h + (chartAt H x).contMDiffAt_extend (chart_mem_maximalAtlas x) h theorem contMDiffAt_extChartAt : ContMDiffAt I 𝓘(𝕜, E) n (extChartAt I x) x := by rw [contMDiffAt_iff_source] @@ -108,7 +109,7 @@ theorem contMDiffAt_extChartAt : ContMDiffAt I 𝓘(𝕜, E) n (extChartAt I x) theorem contMDiffOn_extChartAt [IsManifold I n M] : ContMDiffOn I 𝓘(𝕜, E) n (extChartAt I x) (chartAt H x).source := - contMDiffOn_extend (chart_mem_maximalAtlas x) + (chartAt H x).contMDiffOn_extend (chart_mem_maximalAtlas x) theorem contMDiffOn_extend_symm (he : e ∈ maximalAtlas I n M) : ContMDiffOn 𝓘(𝕜, E) I n (e.extend I).symm (I '' e.target) := by diff --git a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean index 8d9f95219ccf58..c4a9815c985c17 100644 --- a/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean +++ b/Mathlib/Geometry/Manifold/ContMDiff/Defs.lean @@ -69,8 +69,8 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] [NormedAddCommGroup E''] [NormedSpace 𝕜 E''] {H'' : Type*} [TopologicalSpace H''] {I'' : ModelWithCorners 𝕜 E'' H''} {M'' : Type*} [TopologicalSpace M''] [ChartedSpace H'' M''] -- declare functions, sets, points and smoothness indices - {e : OpenPartialHomeomorph M H} - {e' : OpenPartialHomeomorph M' H'} {f f₁ : M → M'} {s s₁ t : Set M} {x : M} {m n : ℕ∞ω} + {e : OpenPartialHomeomorph M H} {e' : OpenPartialHomeomorph M' H'} + {f f₁ : M → M'} {s s₁ t : Set M} {x x' : M} {y : M'} {m n : ℕ∞ω} variable (I I') in /-- Property in the model space of a model with corners of being `C^n` within a set at a point, @@ -160,18 +160,22 @@ theorem contDiffWithinAtProp_id (x : H) : ContDiffWithinAtProp I I n id univ x : · simp only [mfld_simps] variable (I I') in -/-- A function is `n` times continuously differentiable within a set at a point in a manifold if -it is continuous and it is `n` times continuously differentiable in this set around this point, when -read in the preferred chart at this point. +/-- `ContMDiffWithinAt I I' n f x` indicates that the function `f : M → M'` between manifolds +is `n` times continuously differentiable at `x : M` within the set `s`. + +`f` is `n` times continuously differentiable within `s` at `x` if it is continuous and it is `n` +times continuously differentiable in this set around `x`, when read in the preferred chart at `x`. The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/ def ContMDiffWithinAt (n : ℕ∞ω) (f : M → M') (s : Set M) (x : M) := LiftPropWithinAt (ContDiffWithinAtProp I I' n) f s x variable (I I') in -/-- A function is `n` times continuously differentiable at a point in a manifold if -it is continuous and it is `n` times continuously differentiable around this point, when -read in the preferred chart at this point. +/-- `ContMDiffAt I I' n f x` indicates that the function `f : M → M'` between manifolds +is `n` times continuously differentiable at `x : M`. + +`f` is `n` times continuously differentiable at `x` if it is continuous and it is `n` times +continuously differentiable around `x`, when read in the preferred chart at `x`. The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/ def ContMDiffAt (n : ℕ∞ω) (f : M → M') (x : M) := @@ -185,8 +189,11 @@ theorem contMDiffAt_iff {n : ℕ∞ω} {f : M → M'} {x : M} : liftPropAt_iff.trans <| by rw [ContDiffWithinAtProp, preimage_univ, univ_inter]; rfl variable (I I') in -/-- A function is `n` times continuously differentiable in a set of a manifold if it is continuous -and, for any pair of points, it is `n` times continuously differentiable on this set in the charts +/-- `ContMDiffOn I I' n f s` indicates that the function `f : M → M'` between manifolds +is `n` times continuously differentiable in a set `s : Set M`. + +`f` is `n` times continuously differentiable on `s` if it is continuous on `s` and, +for any pair of points, it is `n` times continuously differentiable `s` in the charts around these points. The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/ @@ -194,9 +201,11 @@ def ContMDiffOn (n : ℕ∞ω) (f : M → M') (s : Set M) := ∀ x ∈ s, ContMDiffWithinAt I I' n f s x variable (I I') in -/-- A function is `n` times continuously differentiable in a manifold if it is continuous -and, for any pair of points, it is `n` times continuously differentiable in the charts -around these points. +/-- `ContMDiff I I' n f` indicates that the function `f : M → M'` between manifolds +is `n` times continuously differentiable. + +`f` is `n` times continuously differentiable if it is continuous and, for any pair of points, +it is `n` times continuously differentiable in the charts around these points. The parameter `n` belongs to `ℕ∞ω` (accessible in the `ContDiff` scope), i.e. it can be a natural number, `∞`, or `ω`, where `C^ω` corresponds to analytic functions. -/ def ContMDiff (n : ℕ∞ω) (f : M → M') := @@ -281,36 +290,37 @@ theorem contMDiffAt_iff_target {x : M} : ContinuousAt f x ∧ ContMDiffAt I 𝓘(𝕜, E') n (extChartAt I' (f x) ∘ f) x := by rw [ContMDiffAt, ContMDiffAt, contMDiffWithinAt_iff_target, continuousWithinAt_univ] +theorem continuousWithinAt_iff_source : + ContinuousWithinAt f s x ↔ + ContinuousWithinAt (f ∘ (extChartAt I x).symm) + ((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x) := by + refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ + · apply h.comp_of_eq + · exact (continuousAt_extChartAt_symm x).continuousWithinAt + · exact (mapsTo_preimage _ _).mono_left inter_subset_left + · exact extChartAt_to_inv x + · rw [← continuousWithinAt_inter (extChartAt_source_mem_nhds (I := I) x)] + have : ContinuousWithinAt ((f ∘ ↑(extChartAt I x).symm) ∘ ↑(extChartAt I x)) + (s ∩ (extChartAt I x).source) x := by + apply h.comp (continuousAt_extChartAt x).continuousWithinAt + intro y hy + have : (chartAt H x).symm ((chartAt H x) y) = y := + OpenPartialHomeomorph.left_inv _ (by simpa using hy.2) + simpa [this] using hy.1 + apply this.congr + · intro y hy + have : (chartAt H x).symm ((chartAt H x) y) = y := + OpenPartialHomeomorph.left_inv _ (by simpa using hy.2) + simp [this] + · simp + /-- One can reformulate being `Cⁿ` within a set at a point as being `Cⁿ` in the source space when composing with the extended chart. -/ theorem contMDiffWithinAt_iff_source : ContMDiffWithinAt I I' n f s x ↔ ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (extChartAt I x).symm) ((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x) := by - simp_rw [ContMDiffWithinAt, liftPropWithinAt_iff'] - have : ContinuousWithinAt f s x - ↔ ContinuousWithinAt (f ∘ ↑(extChartAt I x).symm) (↑(extChartAt I x).symm ⁻¹' s ∩ range ↑I) - (extChartAt I x x) := by - refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ - · apply h.comp_of_eq - · exact (continuousAt_extChartAt_symm x).continuousWithinAt - · exact (mapsTo_preimage _ _).mono_left inter_subset_left - · exact extChartAt_to_inv x - · rw [← continuousWithinAt_inter (extChartAt_source_mem_nhds (I := I) x)] - have : ContinuousWithinAt ((f ∘ ↑(extChartAt I x).symm) ∘ ↑(extChartAt I x)) - (s ∩ (extChartAt I x).source) x := by - apply h.comp (continuousAt_extChartAt x).continuousWithinAt - intro y hy - have : (chartAt H x).symm ((chartAt H x) y) = y := - OpenPartialHomeomorph.left_inv _ (by simpa using hy.2) - simpa [this] using hy.1 - apply this.congr - · intro y hy - have : (chartAt H x).symm ((chartAt H x) y) = y := - OpenPartialHomeomorph.left_inv _ (by simpa using hy.2) - simp [this] - · simp - rw [← this] + simp_rw [ContMDiffWithinAt, liftPropWithinAt_iff', ← continuousWithinAt_iff_source] simp only [ContDiffWithinAtProp, mfld_simps, preimage_comp, comp_assoc] /-- One can reformulate being `Cⁿ` at a point as being `Cⁿ` in the source space when @@ -324,7 +334,7 @@ theorem contMDiffAt_iff_source : section IsManifold theorem contMDiffWithinAt_iff_source_of_mem_maximalAtlas - [IsManifold I n M] (he : e ∈ maximalAtlas I n M) (hx : x ∈ e.source) : + (he : e ∈ maximalAtlas I n M) (hx : x ∈ e.source) : ContMDiffWithinAt I I' n f s x ↔ ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (e.extend I).symm) ((e.extend I).symm ⁻¹' s ∩ range I) (e.extend I x) := by @@ -337,41 +347,45 @@ theorem contMDiffWithinAt_iff_source_of_mem_maximalAtlas rfl theorem contMDiffWithinAt_iff_source_of_mem_source - [IsManifold I n M] {x' : M} (hx' : x' ∈ (chartAt H x).source) : + [IsManifold I n M] (hx' : x' ∈ (chartAt H x).source) : ContMDiffWithinAt I I' n f s x' ↔ ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (extChartAt I x).symm) ((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x') := contMDiffWithinAt_iff_source_of_mem_maximalAtlas (chart_mem_maximalAtlas x) hx' theorem contMDiffAt_iff_source_of_mem_source - [IsManifold I n M] {x' : M} (hx' : x' ∈ (chartAt H x).source) : + [IsManifold I n M] (hx' : x' ∈ (chartAt H x).source) : ContMDiffAt I I' n f x' ↔ ContMDiffWithinAt 𝓘(𝕜, E) I' n (f ∘ (extChartAt I x).symm) (range I) (extChartAt I x x') := by simp_rw [ContMDiffAt, contMDiffWithinAt_iff_source_of_mem_source hx', preimage_univ, univ_inter] -theorem contMDiffWithinAt_iff_target_of_mem_source - [IsManifold I' n M'] {x : M} {y : M'} (hy : f x ∈ (chartAt H' y).source) : +theorem contMDiffWithinAt_iff_target_of_mem_maximalAtlas + (he' : e' ∈ maximalAtlas I' n M') (hx : f x ∈ e'.source) : ContMDiffWithinAt I I' n f s x ↔ - ContinuousWithinAt f s x ∧ ContMDiffWithinAt I 𝓘(𝕜, E') n (extChartAt I' y ∘ f) s x := by - simp_rw [ContMDiffWithinAt] - rw [(contDiffWithinAt_localInvariantProp n).liftPropWithinAt_indep_chart_target - (chart_mem_maximalAtlas y) hy, - and_congr_right] - intro hf - simp_rw [StructureGroupoid.liftPropWithinAt_self_target] - simp_rw [((chartAt H' y).continuousAt hy).comp_continuousWithinAt hf] - rw [← extChartAt_source (I := I')] at hy - simp_rw [(continuousAt_extChartAt' hy).comp_continuousWithinAt hf] - rfl + ContinuousWithinAt f s x ∧ ContMDiffWithinAt I 𝓘(𝕜, E') n ((e'.extend I') ∘ f) s x := by + simp_rw [ContMDiffWithinAt, + (contDiffWithinAt_localInvariantProp n).liftPropWithinAt_indep_chart_target he' hx] + apply and_congr_right (fun h ↦ ?_) + have A : ContinuousWithinAt ((e'.extend I') ∘ f) s x := + (e'.continuousAt_extend hx).comp_continuousWithinAt h + have A' : ContinuousWithinAt (e' ∘ f) s x := (e'.continuousAt hx).comp_continuousWithinAt h + simp_rw [StructureGroupoid.liftPropWithinAt_self_target, A, A'] + simp [ContDiffWithinAtProp, comp_assoc] + +theorem contMDiffWithinAt_iff_target_of_mem_source [IsManifold I' n M'] + (hy : f x ∈ (chartAt H' y).source) : + ContMDiffWithinAt I I' n f s x ↔ + ContinuousWithinAt f s x ∧ ContMDiffWithinAt I 𝓘(𝕜, E') n (extChartAt I' y ∘ f) s x := + contMDiffWithinAt_iff_target_of_mem_maximalAtlas (chart_mem_maximalAtlas _) hy -theorem contMDiffAt_iff_target_of_mem_source - [IsManifold I' n M'] {x : M} {y : M'} (hy : f x ∈ (chartAt H' y).source) : +theorem contMDiffAt_iff_target_of_mem_source [IsManifold I' n M'] + (hy : f x ∈ (chartAt H' y).source) : ContMDiffAt I I' n f x ↔ ContinuousAt f x ∧ ContMDiffAt I 𝓘(𝕜, E') n (extChartAt I' y ∘ f) x := by rw [ContMDiffAt, contMDiffWithinAt_iff_target_of_mem_source hy, continuousWithinAt_univ, ContMDiffAt] -theorem contMDiffWithinAt_iff_of_mem_maximalAtlas {x : M} (he : e ∈ maximalAtlas I n M) +theorem contMDiffWithinAt_iff_of_mem_maximalAtlas (he : e ∈ maximalAtlas I n M) (he' : e' ∈ maximalAtlas I' n M') (hx : x ∈ e.source) (hy : f x ∈ e'.source) : ContMDiffWithinAt I I' n f s x ↔ ContinuousWithinAt f s x ∧ @@ -379,10 +393,27 @@ theorem contMDiffWithinAt_iff_of_mem_maximalAtlas {x : M} (he : e ∈ maximalAtl ((e.extend I).symm ⁻¹' s ∩ range I) (e.extend I x) := (contDiffWithinAt_localInvariantProp n).liftPropWithinAt_indep_chart he hx he' hy +/-- An alternative version of `contMDiffWithinAt_iff_of_mem_maximalAtlas` which takes a +chart `e'` in the target in the maximal atlas, but uses the preferred chart on the domain. -/ +theorem contMDiffWithinAt_iff_of_mem_maximalAtlas' + (he' : e' ∈ maximalAtlas I' n M') (hy : f x ∈ e'.source) : + ContMDiffWithinAt I I' n f s x ↔ + ContinuousWithinAt f s x ∧ + ContDiffWithinAt 𝕜 n (e'.extend I' ∘ f ∘ (extChartAt I x).symm) + ((extChartAt I x).symm ⁻¹' s ∩ range I) (extChartAt I x x) := by + rw [contMDiffWithinAt_iff_source, + contMDiffWithinAt_iff_target_of_mem_maximalAtlas he' (by simpa)] + apply and_congr continuousWithinAt_iff_source.symm + -- TODO: this is `contMDiffWithinAt_iff_contDiffWithinAt` copied, + -- which is not put here for import reasons + simp +contextual only [ContMDiffWithinAt, liftPropWithinAt_iff', + ContDiffWithinAtProp, iff_def, mfld_simps] + exact ContDiffWithinAt.continuousWithinAt + /-- An alternative formulation of `contMDiffWithinAt_iff_of_mem_maximalAtlas` -if the set if `s` lies in `e.source`. -/ -theorem contMDiffWithinAt_iff_image {x : M} (he : e ∈ maximalAtlas I n M) - (he' : e' ∈ maximalAtlas I' n M') +if the set `s` lies in `e.source`. -/ +theorem contMDiffWithinAt_iff_image + (he : e ∈ maximalAtlas I n M) (he' : e' ∈ maximalAtlas I' n M') (hs : s ⊆ e.source) (hx : x ∈ e.source) (hy : f x ∈ e'.source) : ContMDiffWithinAt I I' n f s x ↔ ContinuousWithinAt f s x ∧ @@ -395,8 +426,7 @@ theorem contMDiffWithinAt_iff_image {x : M} (he : e ∈ maximalAtlas I n M) /-- One can reformulate being `C^n` within a set at a point as continuity within this set at this point, and being `C^n` in any chart containing that point. -/ theorem contMDiffWithinAt_iff_of_mem_source [IsManifold I n M] [IsManifold I' n M'] - {x' : M} {y : M'} (hx : x' ∈ (chartAt H x).source) - (hy : f x' ∈ (chartAt H' y).source) : + (hx : x' ∈ (chartAt H x).source) (hy : f x' ∈ (chartAt H' y).source) : ContMDiffWithinAt I I' n f s x' ↔ ContinuousWithinAt f s x' ∧ ContDiffWithinAt 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm) @@ -405,8 +435,7 @@ theorem contMDiffWithinAt_iff_of_mem_source [IsManifold I n M] [IsManifold I' n (chart_mem_maximalAtlas y) hx hy theorem contMDiffWithinAt_iff_of_mem_source' [IsManifold I n M] [IsManifold I' n M'] - {x' : M} {y : M'} (hx : x' ∈ (chartAt H x).source) - (hy : f x' ∈ (chartAt H' y).source) : + (hx : x' ∈ (chartAt H x).source) (hy : f x' ∈ (chartAt H' y).source) : ContMDiffWithinAt I I' n f s x' ↔ ContinuousWithinAt f s x' ∧ ContDiffWithinAt 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm) @@ -424,8 +453,7 @@ theorem contMDiffWithinAt_iff_of_mem_source' [IsManifold I n M] [IsManifold I' n exact hc (extChartAt_source_mem_nhds' hy) theorem contMDiffAt_iff_of_mem_source [IsManifold I n M] [IsManifold I' n M'] - {x' : M} {y : M'} (hx : x' ∈ (chartAt H x).source) - (hy : f x' ∈ (chartAt H' y).source) : + (hx : x' ∈ (chartAt H x).source) (hy : f x' ∈ (chartAt H' y).source) : ContMDiffAt I I' n f x' ↔ ContinuousAt f x' ∧ ContDiffWithinAt 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm) (range I) @@ -454,7 +482,7 @@ these charts. Note: this lemma uses `extChartAt I x '' s` instead of `(extChartAt I x).symm ⁻¹' s` to ensure that this set lies in `(extChartAt I x).target`. -/ theorem contMDiffOn_iff_of_subset_source [IsManifold I n M] [IsManifold I' n M'] - {x : M} {y : M'} (hs : s ⊆ (chartAt H x).source) + (hs : s ⊆ (chartAt H x).source) (h2s : MapsTo f s (chartAt H' y).source) : ContMDiffOn I I' n f s ↔ ContinuousOn f s ∧ @@ -468,8 +496,7 @@ these charts. Note: this lemma uses `extChartAt I x '' s` instead of `(extChartAt I x).symm ⁻¹' s` to ensure that this set lies in `(extChartAt I x).target`. -/ theorem contMDiffOn_iff_of_subset_source' [IsManifold I n M] [IsManifold I' n M'] - {x : M} {y : M'} (hs : s ⊆ (extChartAt I x).source) - (h2s : MapsTo f s (extChartAt I' y).source) : + (hs : s ⊆ (extChartAt I x).source) (h2s : MapsTo f s (extChartAt I' y).source) : ContMDiffOn I I' n f s ↔ ContDiffOn 𝕜 n (extChartAt I' y ∘ f ∘ (extChartAt I x).symm) (extChartAt I x '' s) := by rw [extChartAt_source] at hs h2s @@ -505,8 +532,7 @@ theorem contMDiffOn_iff [IsManifold I n M] [IsManifold I' n M'] : mfld_set_tac /-- zero-smoothness on a set is equivalent to continuity on this set. -/ -theorem contMDiffOn_zero_iff : - ContMDiffOn I I' 0 f s ↔ ContinuousOn f s := by +theorem contMDiffOn_zero_iff : ContMDiffOn I I' 0 f s ↔ ContinuousOn f s := by rw [contMDiffOn_iff] refine ⟨fun h ↦ h.1, fun h ↦ ⟨h, ?_⟩⟩ intro x y @@ -713,7 +739,7 @@ protected theorem ContMDiffOn.contMDiffAt (h : ContMDiffOn I I' n f s) (hx : s ContMDiffAt I I' n f x := (h x (mem_of_mem_nhds hx)).contMDiffAt hx -theorem contMDiffOn_iff_source_of_mem_maximalAtlas [IsManifold I n M] +theorem contMDiffOn_iff_source_of_mem_maximalAtlas (he : e ∈ maximalAtlas I n M) (hs : s ⊆ e.source) : ContMDiffOn I I' n f s ↔ ContMDiffOn 𝓘(𝕜, E) I' n (f ∘ (e.extend I).symm) (e.extend I '' s) := by diff --git a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean index d5747cf0dbe93d..9109509853bc05 100644 --- a/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean +++ b/Mathlib/Geometry/Manifold/ContMDiffMFDeriv.lean @@ -69,7 +69,7 @@ protected theorem ContMDiffWithinAt.mfderivWithin {x₀ : N} {f : N → M → M' {t : Set N} {u : Set M} (hf : CMDiffAt[t ×ˢ u] n (Function.uncurry f) (x₀, g x₀)) (hg : CMDiffAt[t] m g x₀) (hx₀ : x₀ ∈ t) - (hu : MapsTo g t u) (hmn : m + 1 ≤ n) (h'u : UniqueMDiffOn I u) : + (hu : MapsTo g t u) (hmn : m + 1 ≤ n) (h'u : UniqueMDiff[u]) : CMDiffAt[t] m (inTangentCoordinates I I' g (fun x ↦ f x (g x)) (fun x ↦ mfderiv[u] (f x) (g x)) x₀) x₀ := by -- first localize the result to a smaller set, to make sure everything happens in chart domains @@ -148,8 +148,7 @@ protected theorem ContMDiffWithinAt.mfderivWithin {x₀ : N} {f : N → M → M' apply nhdsWithin_mono _ ht't filter_upwards [h2f, h4f, h2g, self_mem_nhdsWithin] with x hx h'x h2 hxt have h1 : g x ∈ u := hu hxt - have h3 : UniqueMDiffWithinAt 𝓘(𝕜, E) - ((extChartAt I (g x₀)).target ∩ (extChartAt I (g x₀)).symm ⁻¹' u) + have h3 : UniqueMDiffAt[(extChartAt I (g x₀)).target ∩ (extChartAt I (g x₀)).symm ⁻¹' u] ((extChartAt I (g x₀)) (g x)) := by apply UniqueDiffWithinAt.uniqueMDiffWithinAt apply UniqueMDiffOn.uniqueDiffOn_target_inter h'u @@ -191,7 +190,7 @@ This is a special case of `ContMDiffWithinAt.mfderivWithin` where `f` does not c parameters and `g = id`. -/ theorem ContMDiffWithinAt.mfderivWithin_const {x₀ : M} {f : M → M'} - (hf : CMDiffAt[s] n f x₀) (hmn : m + 1 ≤ n) (hx : x₀ ∈ s) (hs : UniqueMDiffOn I s) : + (hf : CMDiffAt[s] n f x₀) (hmn : m + 1 ≤ n) (hx : x₀ ∈ s) (hs : UniqueMDiff[s]) : CMDiffAt[s] m (inTangentCoordinates I I' id f (mfderiv[s] f) x₀) x₀ := by have : CMDiffAt[s ×ˢ s] n (fun x : M × M ↦ f x.2) (x₀, x₀) := hf.comp (x₀, x₀) contMDiffWithinAt_snd mapsTo_snd_prod @@ -210,7 +209,7 @@ theorem ContMDiffWithinAt.mfderivWithin_apply {x₀ : N'} (hf : CMDiffAt[t ×ˢ u] n (Function.uncurry f) (g₁ x₀, g (g₁ x₀))) (hg : CMDiffAt[t] m g (g₁ x₀)) (hg₁ : CMDiffAt[v] m g₁ x₀) (hg₂ : CMDiffAt[v] m g₂ x₀) (hmn : m + 1 ≤ n) (h'g₁ : MapsTo g₁ v t) - (hg₁x₀ : g₁ x₀ ∈ t) (h'g : MapsTo g t u) (hu : UniqueMDiffOn I u) : + (hg₁x₀ : g₁ x₀ ∈ t) (h'g : MapsTo g t u) (hu : UniqueMDiff[u]) : CMDiffAt[v] m (fun x ↦ (inTangentCoordinates I I' g (fun x ↦ f x (g x)) (fun x ↦ mfderiv[u] (f x) (g x)) (g₁ x₀) (g₁ x)) (g₂ x)) x₀ := ((hf.mfderivWithin hg hg₁x₀ h'g hmn hu).comp_of_eq hg₁ h'g₁ rfl).clm_apply hg₂ @@ -273,14 +272,13 @@ variable [Is : IsManifold I 1 M] [I's : IsManifold I' 1 M'] /-- If a function is `C^n` on a domain with unique derivatives, then its bundled derivative is `C^m` when `m+1 ≤ n`. -/ theorem ContMDiffOn.contMDiffOn_tangentMapWithin - (hf : CMDiff[s] n f) (hmn : m + 1 ≤ n) (hs : UniqueMDiffOn I s) : + (hf : CMDiff[s] n f) (hmn : m + 1 ≤ n) (hs : UniqueMDiff[s]) : CMDiff[(π E (TangentSpace I) ⁻¹' s)] m (tangentMap[s] f) := by intro x₀ hx₀ let s' : Set (TangentBundle I M) := (π E (TangentSpace I) ⁻¹' s) let b₁ : TangentBundle I M → M := fun p ↦ p.1 let v : Π (y : TangentBundle I M), TangentSpace% (b₁ y) := fun y ↦ y.2 - have hv : ContMDiffWithinAt I.tangent I.tangent m (fun y ↦ (v y : TangentBundle I M)) s' x₀ := - contMDiffWithinAt_id + have hv : CMDiffAt[s'] m (fun y ↦ (v y : TangentBundle I M)) x₀ := contMDiffWithinAt_id let b₂ : TangentBundle I M → M' := f ∘ b₁ have hb₂ : CMDiffAt[s'] m b₂ x₀ := ((hf (b₁ x₀) hx₀).of_le (le_self_add.trans hmn)).comp _ @@ -298,7 +296,7 @@ theorem ContMDiffOn.contMDiffOn_tangentMapWithin /-- If a function is `C^n` on a domain with unique derivatives, with `1 ≤ n`, then its bundled derivative is continuous there. -/ theorem ContMDiffOn.continuousOn_tangentMapWithin (hf : CMDiff[s] n f) (hmn : 1 ≤ n) - (hs : UniqueMDiffOn I s) : + (hs : UniqueMDiff[s]) : ContinuousOn (tangentMap[s] f) (π E (TangentSpace I) ⁻¹' s) := by have : CMDiff[π E (TangentSpace I) ⁻¹' s] 0 (tangentMap[s] f) := hf.contMDiffOn_tangentMapWithin hmn hs diff --git a/Mathlib/Geometry/Manifold/Diffeomorph.lean b/Mathlib/Geometry/Manifold/Diffeomorph.lean index 0e6e6e8fc38027..8aec58763bbdb0 100644 --- a/Mathlib/Geometry/Manifold/Diffeomorph.lean +++ b/Mathlib/Geometry/Manifold/Diffeomorph.lean @@ -10,6 +10,7 @@ public import Mathlib.Geometry.Manifold.MFDeriv.UniqueDifferential /-! # Diffeomorphisms + This file implements diffeomorphisms. ## Definitions @@ -338,19 +339,19 @@ theorem toOpenPartialHomeomorph_mdifferentiable (h : M ≃ₘ^n⟮I, J⟯ N) (hn ⟨h.mdifferentiableOn _ hn, h.symm.mdifferentiableOn _ hn⟩ theorem uniqueMDiffOn_image_aux (h : M ≃ₘ^n⟮I, J⟯ N) (hn : n ≠ 0) {s : Set M} - (hs : UniqueMDiffOn I s) : UniqueMDiffOn J (h '' s) := by + (hs : UniqueMDiff[s]) : UniqueMDiff[h '' s] := by convert! hs.uniqueMDiffOn_preimage (h.toOpenPartialHomeomorph_mdifferentiable hn) simp [h.image_eq_preimage_symm] @[simp] theorem uniqueMDiffOn_image (h : M ≃ₘ^n⟮I, J⟯ N) (hn : n ≠ 0) {s : Set M} : - UniqueMDiffOn J (h '' s) ↔ UniqueMDiffOn I s := + UniqueMDiff[h '' s] ↔ UniqueMDiff[s] := ⟨fun hs => h.symm_image_image s ▸ h.symm.uniqueMDiffOn_image_aux hn hs, h.uniqueMDiffOn_image_aux hn⟩ @[simp] theorem uniqueMDiffOn_preimage (h : M ≃ₘ^n⟮I, J⟯ N) (hn : n ≠ 0) {s : Set N} : - UniqueMDiffOn I (h ⁻¹' s) ↔ UniqueMDiffOn J s := + UniqueMDiff[h ⁻¹' s] ↔ UniqueMDiff[s] := h.symm_image_eq_preimage s ▸ h.symm.uniqueMDiffOn_image hn @[simp] diff --git a/Mathlib/Geometry/Manifold/Immersion.lean b/Mathlib/Geometry/Manifold/Immersion.lean index 852f775a9a4acd..8d81f03d5e84e4 100644 --- a/Mathlib/Geometry/Manifold/Immersion.lean +++ b/Mathlib/Geometry/Manifold/Immersion.lean @@ -54,6 +54,7 @@ This shortens the overall argument, as the definition of submersions has the sam * `IsImmersion.id`: the identity map is an immersion * `IsImmersion.of_opens`: the inclusion of an open subset `s → M` of a smooth manifold is a smooth immersion +* `ModelWithCorners.isImmersion`: every model with corners is itself an immersion * `IsImmersionOfComplement.sumInl` and `IsImmersionOfComplement.sumInr`: given `C^n` manifolds `M` and `N`, `Sum.inl : M → M ⊕ N` and `Sum.inr : N → M ⊕ N` are `C^n` immersions * `IsImmersionAt.contMDiffAt`: if f is an immersion at `x`, it is `C^n` at `x`. @@ -391,6 +392,14 @@ lemma of_opens [IsManifold I n M] (s : TopologicalSpace.Opens M) (y : s) : suffices I ((chartAt H ↑y) ((chartAt H y).symm (I.symm x))) = x by simpa +contextual simp_all +/-- Every `ModelWithCorners 𝕜 E H` is an immersion when viewed as a map `H → E`. -/ +protected lemma _root_.ModelWithCorners.isImmersionAtOfComplement {n : ℕ} {x : H} : + IsImmersionAtOfComplement PUnit I 𝓘(𝕜, E) n I x := + Manifold.IsImmersionAtOfComplement.mk_of_continuousAt I.continuousAt + (.prodUnique _ _ _) (.refl _) (.refl _) (by simp) (by simp) + (IsManifold.subset_maximalAtlas (by simp)) (IsManifold.subset_maximalAtlas (by simp)) + (by simp [Function.comp_def]) + @[deprecated (since := "2025-12-16")] alias ofOpen := of_opens /-- Prefer using `IsImmersionAtOfComplement.continuousAt` instead -/ @@ -589,6 +598,12 @@ lemma of_opens [IsManifold I n M] (s : TopologicalSpace.Opens M) (hx : x ∈ s) @[deprecated (since := "2025-12-16")] alias ofOpen := of_opens +/-- Every `ModelWithCorners 𝕜 E H` is an immersion when viewed as a map `H → E`. -/ +protected lemma _root_.ModelWithCorners.isImmersionAt {n : ℕ} {x : H} : + IsImmersionAt I (modelWithCornersSelf 𝕜 E) n I x := by + use PUnit, by infer_instance, by infer_instance + exact I.isImmersionAtOfComplement + /-- Prefer using `IsImmersionAt.continuousAt` instead -/ theorem continuousOn (h : IsImmersionAt I J n f x) : ContinuousOn f h.domChart.source := h.isImmersionAtOfComplement_complement.continuousOn @@ -699,6 +714,11 @@ lemma of_opens [IsManifold I n M] (s : TopologicalSpace.Opens M) : IsImmersionOfComplement PUnit I I n (Subtype.val : s → M) := fun y ↦ IsImmersionAtOfComplement.of_opens s y +/-- Every `ModelWithCorners 𝕜 E H` is an immersion when viewed as a map `H → E`. -/ +protected lemma _root_.ModelWithCorners.isImmersionOfComplement {n : ℕ} : + IsImmersionOfComplement PUnit I (modelWithCornersSelf 𝕜 E) n I := + fun _ ↦ I.isImmersionAtOfComplement + /-- Given `C^n` manifolds `M` and `N` over the same model `I`, `Sum.inl : M → M ⊕ N` is a `C^n` immersion with complement `Unit` -/ lemma sumInl {M' : Type*} [TopologicalSpace M'] [ChartedSpace H M'] [IsManifold I n M] @@ -794,6 +814,12 @@ lemma of_opens [IsManifold I n M] (s : TopologicalSpace.Opens M) : @[deprecated (since := "2025-12-16")] alias ofOpen := of_opens +/-- Every `ModelWithCorners 𝕜 E H` is an immersion when viewed as a map `H → E`. -/ +protected lemma _root_.ModelWithCorners.isImmersion {n : ℕ} : + IsImmersion I (modelWithCornersSelf 𝕜 E) n I := by + use PUnit, by infer_instance, by infer_instance + exact I.isImmersionOfComplement + /-- A `C^n` immersion is `C^n`. -/ theorem contMDiff (h : IsImmersion I J n f) : CMDiff n f := diff --git a/Mathlib/Geometry/Manifold/Instances/Real.lean b/Mathlib/Geometry/Manifold/Instances/Real.lean index 5e11d5245691c4..0ac71a8e87b7f7 100644 --- a/Mathlib/Geometry/Manifold/Instances/Real.lean +++ b/Mathlib/Geometry/Manifold/Instances/Real.lean @@ -108,11 +108,11 @@ instance EuclideanHalfSpace.pathConnectedSpace [NeZero n] : instance EuclideanQuadrant.pathConnectedSpace : PathConnectedSpace (EuclideanQuadrant n) := isPathConnected_iff_pathConnectedSpace.mp <| convex.isPathConnected ⟨0, by simp⟩ -instance [NeZero n] : LocPathConnectedSpace (EuclideanHalfSpace n) := - EuclideanHalfSpace.convex.locPathConnectedSpace +instance [NeZero n] : LocallyPathConnectedSpace (EuclideanHalfSpace n) := + EuclideanHalfSpace.convex.locallyPathConnectedSpace -instance : LocPathConnectedSpace (EuclideanQuadrant n) := - EuclideanQuadrant.convex.locPathConnectedSpace +instance : LocallyPathConnectedSpace (EuclideanQuadrant n) := + EuclideanQuadrant.convex.locallyPathConnectedSpace theorem range_euclideanHalfSpace (n : ℕ) [NeZero n] : range (Subtype.val : EuclideanHalfSpace n → _) = { y | 0 ≤ y 0 } := diff --git a/Mathlib/Geometry/Manifold/IsManifold/InteriorBoundary.lean b/Mathlib/Geometry/Manifold/IsManifold/InteriorBoundary.lean index 6673d261c8b031..08e68119c83dde 100644 --- a/Mathlib/Geometry/Manifold/IsManifold/InteriorBoundary.lean +++ b/Mathlib/Geometry/Manifold/IsManifold/InteriorBoundary.lean @@ -13,6 +13,7 @@ import Mathlib.Analysis.LocallyConvex.Separation /-! # Interior and boundary of a manifold + Define the interior and boundary of a manifold. ## Main definitions diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean b/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean index 70a61d9b9c7dba..2b06e0a39ea983 100644 --- a/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean +++ b/Mathlib/Geometry/Manifold/MFDeriv/Atlas.lean @@ -269,7 +269,7 @@ lemma mfderiv_extChartAt_comp_mfderivWithin_extChartAt_symm {x : M} {y : E} (hy : y ∈ (extChartAt I x).target) : (mfderiv% (extChartAt I x) ((extChartAt I x).symm y)) ∘L (mfderiv[range I] (extChartAt I x).symm y) = ContinuousLinearMap.id _ _ := by - have U : UniqueMDiffWithinAt 𝓘(𝕜, E) (range ↑I) y := by + have U : UniqueMDiffAt[range I] y := by apply I.uniqueMDiffOn exact extChartAt_target_subset_range x hy have h'y : (extChartAt I x).symm y ∈ (extChartAt I x).source := (extChartAt I x).map_target hy @@ -306,7 +306,7 @@ lemma mfderivWithin_extChartAt_symm_comp_mfderiv_extChartAt have h'y : (extChartAt I x).symm y ∈ (extChartAt I x).source := (extChartAt I x).map_target hy have h''y : (extChartAt I x).symm y ∈ (chartAt H x).source := by rwa [← extChartAt_source (I := I)] - have U' : UniqueMDiffWithinAt I (extChartAt I x).source ((extChartAt I x).symm y) := + have U' : UniqueMDiffAt[(extChartAt I x).source] ((extChartAt I x).symm y) := (isOpen_extChartAt_source x).uniqueMDiffWithinAt h'y have : mfderiv% (extChartAt I x) ((extChartAt I x).symm y) = mfderiv[(extChartAt I x).source] (extChartAt I x) ((extChartAt I x).symm y) := by diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean b/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean index b6b72c2c4f437f..04fc8658e10cff 100644 --- a/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean +++ b/Mathlib/Geometry/Manifold/MFDeriv/Basic.lean @@ -8,6 +8,7 @@ module public import Mathlib.Analysis.Calculus.TangentCone.Prod public import Mathlib.Geometry.Manifold.MFDeriv.Defs public import Mathlib.Geometry.Manifold.ContMDiff.Defs +import Mathlib.Geometry.Manifold.Notation /-! # Basic properties of the manifold Fréchet derivative @@ -49,7 +50,7 @@ variable {M'' : Type*} [TopologicalSpace M''] [ChartedSpace H'' M''] {f f₁ : M → M'} {x : M} {s t : Set M} {g : M' → M''} {u : Set M'} -theorem uniqueMDiffWithinAt_univ : UniqueMDiffWithinAt I univ x := by +theorem uniqueMDiffWithinAt_univ : UniqueMDiffAt[(univ : Set M)] x := by unfold UniqueMDiffWithinAt simp only [preimage_univ, univ_inter] exact I.uniqueDiffOn _ (mem_range_self _) @@ -57,57 +58,57 @@ theorem uniqueMDiffWithinAt_univ : UniqueMDiffWithinAt I univ x := by variable {I} theorem uniqueMDiffWithinAt_iff_inter_range {s : Set M} {x : M} : - UniqueMDiffWithinAt I s x ↔ + UniqueMDiffAt[s] x ↔ UniqueDiffWithinAt 𝕜 ((extChartAt I x).symm ⁻¹' s ∩ range I) ((extChartAt I x) x) := Iff.rfl theorem uniqueMDiffWithinAt_iff {s : Set M} {x : M} : - UniqueMDiffWithinAt I s x ↔ + UniqueMDiffAt[s] x ↔ UniqueDiffWithinAt 𝕜 ((extChartAt I x).symm ⁻¹' s ∩ (extChartAt I x).target) ((extChartAt I x) x) := by apply uniqueDiffWithinAt_congr rw [nhdsWithin_inter, nhdsWithin_inter, nhdsWithin_extChartAt_target_eq] -nonrec theorem UniqueMDiffWithinAt.mono_nhds {s t : Set M} {x : M} (hs : UniqueMDiffWithinAt I s x) - (ht : 𝓝[s] x ≤ 𝓝[t] x) : UniqueMDiffWithinAt I t x := +nonrec theorem UniqueMDiffWithinAt.mono_nhds {s t : Set M} {x : M} (hs : UniqueMDiffAt[s] x) + (ht : 𝓝[s] x ≤ 𝓝[t] x) : UniqueMDiffAt[t] x := hs.mono_nhds <| by simpa only [← map_extChartAt_nhdsWithin] using Filter.map_mono ht theorem UniqueMDiffWithinAt.mono_of_mem_nhdsWithin {s t : Set M} {x : M} - (hs : UniqueMDiffWithinAt I s x) (ht : t ∈ 𝓝[s] x) : UniqueMDiffWithinAt I t x := + (hs : UniqueMDiffAt[s] x) (ht : t ∈ 𝓝[s] x) : UniqueMDiffAt[t] x := hs.mono_nhds (nhdsWithin_le_iff.2 ht) -theorem UniqueMDiffWithinAt.mono (h : UniqueMDiffWithinAt I s x) (st : s ⊆ t) : - UniqueMDiffWithinAt I t x := +theorem UniqueMDiffWithinAt.mono (h : UniqueMDiffAt[s] x) (st : s ⊆ t) : + UniqueMDiffAt[t] x := UniqueDiffWithinAt.mono h <| inter_subset_inter (preimage_mono st) (Subset.refl _) -theorem UniqueMDiffWithinAt.inter' (hs : UniqueMDiffWithinAt I s x) (ht : t ∈ 𝓝[s] x) : - UniqueMDiffWithinAt I (s ∩ t) x := +theorem UniqueMDiffWithinAt.inter' (hs : UniqueMDiffAt[s] x) (ht : t ∈ 𝓝[s] x) : + UniqueMDiffAt[s ∩ t] x := hs.mono_of_mem_nhdsWithin (Filter.inter_mem self_mem_nhdsWithin ht) -theorem UniqueMDiffWithinAt.inter (hs : UniqueMDiffWithinAt I s x) (ht : t ∈ 𝓝 x) : - UniqueMDiffWithinAt I (s ∩ t) x := +theorem UniqueMDiffWithinAt.inter (hs : UniqueMDiffAt[s] x) (ht : t ∈ 𝓝 x) : + UniqueMDiffAt[s ∩ t] x := hs.inter' (nhdsWithin_le_nhds ht) -theorem IsOpen.uniqueMDiffWithinAt (hs : IsOpen s) (xs : x ∈ s) : UniqueMDiffWithinAt I s x := +theorem IsOpen.uniqueMDiffWithinAt (hs : IsOpen s) (xs : x ∈ s) : UniqueMDiffAt[s] x := (uniqueMDiffWithinAt_univ I).mono_of_mem_nhdsWithin <| nhdsWithin_le_nhds <| hs.mem_nhds xs -theorem UniqueMDiffOn.inter (hs : UniqueMDiffOn I s) (ht : IsOpen t) : UniqueMDiffOn I (s ∩ t) := +theorem UniqueMDiffOn.inter (hs : UniqueMDiff[s]) (ht : IsOpen t) : UniqueMDiff[s ∩ t] := fun _x hx => UniqueMDiffWithinAt.inter (hs _ hx.1) (ht.mem_nhds hx.2) -theorem IsOpen.uniqueMDiffOn (hs : IsOpen s) : UniqueMDiffOn I s := +theorem IsOpen.uniqueMDiffOn (hs : IsOpen s) : UniqueMDiff[s] := fun _x hx => hs.uniqueMDiffWithinAt hx -theorem uniqueMDiffOn_univ : UniqueMDiffOn I (univ : Set M) := +theorem uniqueMDiffOn_univ : UniqueMDiff[(univ : Set M)] := isOpen_univ.uniqueMDiffOn -nonrec theorem UniqueMDiffWithinAt.prod {x : M} {y : M'} {s t} (hs : UniqueMDiffWithinAt I s x) - (ht : UniqueMDiffWithinAt I' t y) : UniqueMDiffWithinAt (I.prod I') (s ×ˢ t) (x, y) := by +nonrec theorem UniqueMDiffWithinAt.prod {x : M} {y : M'} {s : Set M} {t : Set M'} + (hs : UniqueMDiffAt[s] x) (ht : UniqueMDiffAt[t] y) : UniqueMDiffAt[s ×ˢ t] (x, y) := by refine (hs.prod ht).mono ?_ rw [ModelWithCorners.range_prod, ← prod_inter_prod] rfl -theorem UniqueMDiffOn.prod {s : Set M} {t : Set M'} (hs : UniqueMDiffOn I s) - (ht : UniqueMDiffOn I' t) : UniqueMDiffOn (I.prod I') (s ×ˢ t) := fun x h ↦ +theorem UniqueMDiffOn.prod {s : Set M} {t : Set M'} (hs : UniqueMDiff[s]) + (ht : UniqueMDiff[t]) : UniqueMDiff[s ×ˢ t] := fun x h ↦ (hs x.1 h.1).prod (ht x.2 h.2) theorem MDifferentiableWithinAt.mono (hst : s ⊆ t) (h : MDifferentiableWithinAt I I' f t x) : @@ -241,7 +242,7 @@ variable {e : OpenPartialHomeomorph M H} {e' : OpenPartialHomeomorph M' H'} open IsManifold theorem mdifferentiableWithinAt_iff_source_of_mem_maximalAtlas - [IsManifold I 1 M] (he : e ∈ maximalAtlas I 1 M) (hx : x ∈ e.source) : + (he : e ∈ maximalAtlas I 1 M) (hx : x ∈ e.source) : MDifferentiableWithinAt I I' f s x ↔ MDifferentiableWithinAt 𝓘(𝕜, E) I' (f ∘ (e.extend I).symm) ((e.extend I).symm ⁻¹' s ∩ range I) (e.extend I x) := by @@ -517,12 +518,12 @@ variable {f' f₀' f₁' : TangentSpace I x →L[𝕜] TangentSpace I' (f x)} set_option backward.isDefEq.respectTransparency false in /-- `UniqueMDiffWithinAt` achieves its goal: it implies the uniqueness of the derivative. -/ -protected nonrec theorem UniqueMDiffWithinAt.eq (U : UniqueMDiffWithinAt I s x) +protected nonrec theorem UniqueMDiffWithinAt.eq (U : UniqueMDiffAt[s] x) (h : HasMFDerivWithinAt I I' f s x f') (h₁ : HasMFDerivWithinAt I I' f s x f₁') : f' = f₁' := by -- `by apply` because the instances can be found in the term but not in the goal. apply U.eq h.2 h₁.2 -protected theorem UniqueMDiffOn.eq (U : UniqueMDiffOn I s) (hx : x ∈ s) +protected theorem UniqueMDiffOn.eq (U : UniqueMDiff[s]) (hx : x ∈ s) (h : HasMFDerivWithinAt I I' f s x f') (h₁ : HasMFDerivWithinAt I I' f s x f₁') : f' = f₁' := UniqueMDiffWithinAt.eq (U _ hx) h h₁ @@ -685,7 +686,7 @@ protected theorem HasMFDerivAt.mfderiv (h : HasMFDerivAt I I' f x f') : mfderiv (hasMFDerivAt_unique h h.mdifferentiableAt.hasMFDerivAt).symm protected theorem HasMFDerivWithinAt.mfderivWithin (h : HasMFDerivWithinAt I I' f s x f') - (hxs : UniqueMDiffWithinAt I s x) : mfderivWithin I I' f s x = f' := by + (hxs : UniqueMDiffAt[s] x) : mfderivWithin I I' f s x = f' := by ext rw [hxs.eq h h.mdifferentiableWithinAt.hasMFDerivWithinAt] @@ -698,11 +699,11 @@ theorem HasMFDerivWithinAt.mfderivWithin_eq_zero (h : HasMFDerivWithinAt I I' f exact h.2 theorem MDifferentiable.mfderivWithin (h : MDifferentiableAt I I' f x) - (hxs : UniqueMDiffWithinAt I s x) : mfderivWithin I I' f s x = mfderiv I I' f x := by + (hxs : UniqueMDiffAt[s] x) : mfderivWithin I I' f s x = mfderiv I I' f x := by apply HasMFDerivWithinAt.mfderivWithin _ hxs exact h.hasMFDerivAt.hasMFDerivWithinAt -theorem mfderivWithin_subset (st : s ⊆ t) (hs : UniqueMDiffWithinAt I s x) +theorem mfderivWithin_subset (st : s ⊆ t) (hs : UniqueMDiffAt[s] x) (h : MDifferentiableWithinAt I I' f t x) : mfderivWithin I I' f s x = mfderivWithin I I' f t x := ((MDifferentiableWithinAt.hasMFDerivWithinAt h).mono st).mfderivWithin hs @@ -755,7 +756,7 @@ theorem hasMFDerivWithinAt_sdiff_singleton (y : M) : @[deprecated (since := "2026-06-03")] alias hasMFDerivWithinAt_diff_singleton := hasMFDerivWithinAt_sdiff_singleton -theorem mfderivWithin_eq_mfderiv (hs : UniqueMDiffWithinAt I s x) (h : MDifferentiableAt I I' f x) : +theorem mfderivWithin_eq_mfderiv (hs : UniqueMDiffAt[s] x) (h : MDifferentiableAt I I' f x) : mfderivWithin I I' f s x = mfderiv I I' f x := by rw [← mfderivWithin_univ] exact mfderivWithin_subset (subset_univ _) hs h.mdifferentiableWithinAt @@ -839,7 +840,7 @@ theorem HasMFDerivAt.continuousAt (h : HasMFDerivAt I I' f x f') : ContinuousAt h.1 theorem tangentMapWithin_subset {p : TangentBundle I M} (st : s ⊆ t) - (hs : UniqueMDiffWithinAt I s p.1) (h : MDifferentiableWithinAt I I' f t p.1) : + (hs : UniqueMDiffAt[s] p.1) (h : MDifferentiableWithinAt I I' f t p.1) : tangentMapWithin I I' f s p = tangentMapWithin I I' f t p := by simp only [tangentMapWithin, mfld_simps] rw [mfderivWithin_subset st hs h] @@ -848,7 +849,7 @@ theorem tangentMapWithin_univ : tangentMapWithin I I' f univ = tangentMap I I' f ext p : 1 simp only [tangentMapWithin, tangentMap, mfld_simps] -theorem tangentMapWithin_eq_tangentMap {p : TangentBundle I M} (hs : UniqueMDiffWithinAt I s p.1) +theorem tangentMapWithin_eq_tangentMap {p : TangentBundle I M} (hs : UniqueMDiffAt[s] p.1) (h : MDifferentiableAt I I' f p.1) : tangentMapWithin I I' f s p = tangentMap I I' f p := by rw [← mdifferentiableWithinAt_univ] at h rw [← tangentMapWithin_univ] @@ -1054,17 +1055,17 @@ theorem MDifferentiableAt.congr_of_eventuallyEq (h : MDifferentiableAt I I' f x) (h.hasMFDerivAt.congr_of_eventuallyEq hL).mdifferentiableAt theorem MDifferentiableWithinAt.mfderivWithin_congr_mono (h : MDifferentiableWithinAt I I' f s x) - (hs : ∀ x ∈ t, f₁ x = f x) (hx : f₁ x = f x) (hxt : UniqueMDiffWithinAt I t x) (h₁ : t ⊆ s) : + (hs : ∀ x ∈ t, f₁ x = f x) (hx : f₁ x = f x) (hxt : UniqueMDiffAt[t] x) (h₁ : t ⊆ s) : mfderivWithin I I' f₁ t x = mfderivWithin I I' f s x := (HasMFDerivWithinAt.congr_mono h.hasMFDerivWithinAt hs hx h₁).mfderivWithin hxt theorem MDifferentiableWithinAt.mfderivWithin_mono (h : MDifferentiableWithinAt I I' f s x) - (hxt : UniqueMDiffWithinAt I t x) (h₁ : t ⊆ s) : + (hxt : UniqueMDiffAt[t] x) (h₁ : t ⊆ s) : mfderivWithin I I' f t x = mfderivWithin I I' f s x := h.mfderivWithin_congr_mono (fun _ _ ↦ rfl) rfl hxt h₁ theorem MDifferentiableWithinAt.mfderivWithin_mono_of_mem_nhdsWithin - (h : MDifferentiableWithinAt I I' f s x) (hxt : UniqueMDiffWithinAt I t x) (h₁ : s ∈ 𝓝[t] x) : + (h : MDifferentiableWithinAt I I' f s x) (hxt : UniqueMDiffAt[t] x) (h₁ : s ∈ 𝓝[t] x) : mfderivWithin I I' f t x = mfderivWithin I I' f s x := (HasMFDerivWithinAt.mono_of_mem_nhdsWithin h.hasMFDerivWithinAt h₁).mfderivWithin hxt @@ -1207,14 +1208,14 @@ theorem MDifferentiableAt.comp_mdifferentiableWithinAt_of_eq {y : M'} subst hy; exact hg.comp_mdifferentiableWithinAt _ hf theorem mfderivWithin_comp (hg : MDifferentiableWithinAt I' I'' g u (f x)) - (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffWithinAt I s x) : + (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffAt[s] x) : mfderivWithin I I'' (g ∘ f) s x = (mfderivWithin I' I'' g u (f x)).comp (mfderivWithin I I' f s x) := by apply HasMFDerivWithinAt.mfderivWithin _ hxs exact HasMFDerivWithinAt.comp x hg.hasMFDerivWithinAt hf.hasMFDerivWithinAt h theorem mfderivWithin_comp_of_eq {x : M} {y : M'} (hg : MDifferentiableWithinAt I' I'' g u y) - (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffWithinAt I s x) + (hf : MDifferentiableWithinAt I I' f s x) (h : s ⊆ f ⁻¹' u) (hxs : UniqueMDiffAt[s] x) (hy : f x = y) : mfderivWithin I I'' (g ∘ f) s x = (mfderivWithin I' I'' g u y).comp (mfderivWithin I I' f s x) := by @@ -1223,7 +1224,7 @@ theorem mfderivWithin_comp_of_eq {x : M} {y : M'} (hg : MDifferentiableWithinAt theorem mfderivWithin_comp_of_preimage_mem_nhdsWithin (hg : MDifferentiableWithinAt I' I'' g u (f x)) (hf : MDifferentiableWithinAt I I' f s x) (h : f ⁻¹' u ∈ 𝓝[s] x) - (hxs : UniqueMDiffWithinAt I s x) : + (hxs : UniqueMDiffAt[s] x) : mfderivWithin I I'' (g ∘ f) s x = (mfderivWithin I' I'' g u (f x)).comp (mfderivWithin I I' f s x) := by have A : s ∩ f ⁻¹' u ∈ 𝓝[s] x := Filter.inter_mem self_mem_nhdsWithin h @@ -1238,20 +1239,20 @@ theorem mfderivWithin_comp_of_preimage_mem_nhdsWithin theorem mfderivWithin_comp_of_preimage_mem_nhdsWithin_of_eq {y : M'} (hg : MDifferentiableWithinAt I' I'' g u y) (hf : MDifferentiableWithinAt I I' f s x) (h : f ⁻¹' u ∈ 𝓝[s] x) - (hxs : UniqueMDiffWithinAt I s x) (hy : f x = y) : + (hxs : UniqueMDiffAt[s] x) (hy : f x = y) : mfderivWithin I I'' (g ∘ f) s x = (mfderivWithin I' I'' g u y).comp (mfderivWithin I I' f s x) := by subst hy; exact mfderivWithin_comp_of_preimage_mem_nhdsWithin _ hg hf h hxs theorem mfderiv_comp_mfderivWithin (hg : MDifferentiableAt I' I'' g (f x)) - (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffWithinAt I s x) : + (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffAt[s] x) : mfderivWithin I I'' (g ∘ f) s x = (mfderiv I' I'' g (f x)).comp (mfderivWithin I I' f s x) := by rw [← mfderivWithin_univ] exact mfderivWithin_comp _ hg.mdifferentiableWithinAt hf (by simp) hxs theorem mfderiv_comp_mfderivWithin_of_eq {x : M} {y : M'} (hg : MDifferentiableAt I' I'' g y) - (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffWithinAt I s x) (hy : f x = y) : + (hf : MDifferentiableWithinAt I I' f s x) (hxs : UniqueMDiffAt[s] x) (hy : f x = y) : mfderivWithin I I'' (g ∘ f) s x = (mfderiv I' I'' g y).comp (mfderivWithin I I' f s x) := by subst hy; exact mfderiv_comp_mfderivWithin x hg hf hxs @@ -1291,7 +1292,7 @@ theorem MDifferentiable.comp (hg : MDifferentiable I' I'' g) (hf : MDifferentiab theorem tangentMapWithin_comp_at (p : TangentBundle I M) (hg : MDifferentiableWithinAt I' I'' g u (f p.1)) (hf : MDifferentiableWithinAt I I' f s p.1) - (h : s ⊆ f ⁻¹' u) (hps : UniqueMDiffWithinAt I s p.1) : + (h : s ⊆ f ⁻¹' u) (hps : UniqueMDiffAt[s] p.1) : tangentMapWithin I I'' (g ∘ f) s p = tangentMapWithin I' I'' g u (tangentMapWithin I I' f s p) := by simp only [tangentMapWithin, mfld_simps] diff --git a/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean b/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean index f7e08d8e323f6f..886ab2f23b95bb 100644 --- a/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean +++ b/Mathlib/Geometry/Manifold/MFDeriv/Defs.lean @@ -316,9 +316,9 @@ def HasMFDerivAt (f : M → M') (x : M) (f' : TangentSpace I x →L[𝕜] Tangen open Classical in variable (I I') in -/-- Let `f` be a function between two manifolds. Then `mfderivWithin I I' f s x` is the -derivative of `f` at `x` within `s`, as a continuous linear map from the tangent space at `x` to the -tangent space at `f x`. -/ +/-- `mfderivWithin I I' f s x`, given a function `f` between two manifolds, +is the derivative of `f` at `x` within `s`, +as a continuous linear map from the tangent space at `x` to the tangent space at `f x`. -/ def mfderivWithin (f : M → M') (s : Set M) (x : M) : TangentSpace I x →L[𝕜] TangentSpace I' (f x) := if MDifferentiableWithinAt I I' f s x then (fderivWithin 𝕜 (writtenInExtChartAt I I' x f) ((extChartAt I x).symm ⁻¹' s ∩ range I) @@ -328,21 +328,22 @@ def mfderivWithin (f : M → M') (s : Set M) (x : M) : TangentSpace I x →L[ open Classical in variable (I I') in -/-- Let `f` be a function between two manifolds. Then `mfderiv I I' f x` is the derivative of -`f` at `x`, as a continuous linear map from the tangent space at `x` to the tangent space at -`f x`. -/ +/-- `mfderiv I I' f x`, given a function `f` between two manifolds, is the derivative of `f` at `x`, +as a continuous linear map from the tangent space at `x` to the tangent space at `f x`. -/ def mfderiv (f : M → M') (x : M) : TangentSpace I x →L[𝕜] TangentSpace I' (f x) := if MDifferentiableAt I I' f x then (fderivWithin 𝕜 (writtenInExtChartAt I I' x f : E → E') (range I) ((extChartAt I x) x) :) else 0 variable (I I') in -/-- The derivative within a set, as a map between the tangent bundles -/ +/-- `tangentMapWithin I I' f s` is the derivative of `f : M → M'` within a set `s`, +as a map between the tangent bundles `TM` and `TM'`. -/ def tangentMapWithin (f : M → M') (s : Set M) : TangentBundle I M → TangentBundle I' M' := fun p => ⟨f p.1, (mfderivWithin I I' f s p.1 : TangentSpace I p.1 → TangentSpace I' (f p.1)) p.2⟩ variable (I I') in -/-- The derivative, as a map between the tangent bundles -/ +/-- `tangentMap I I' f` is the derivative of `f : M → M'` as a map between the tangent bundles +`TM` and `TM'`. -/ def tangentMap (f : M → M') : TangentBundle I M → TangentBundle I' M' := fun p => ⟨f p.1, (mfderiv I I' f p.1 : TangentSpace I p.1 → TangentSpace I' (f p.1)) p.2⟩ diff --git a/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean b/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean index 453ec2e412a4a7..f0d5d10514ba8c 100644 --- a/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean +++ b/Mathlib/Geometry/Manifold/MFDeriv/FDeriv.lean @@ -29,36 +29,37 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] {E : Type*} [NormedAddCom section MFDerivFDeriv theorem uniqueMDiffWithinAt_iff_uniqueDiffWithinAt : - UniqueMDiffWithinAt 𝓘(𝕜, E) s x ↔ UniqueDiffWithinAt 𝕜 s x := by + UniqueMDiffAt[s] x ↔ UniqueDiffWithinAt 𝕜 s x := by simp only [UniqueMDiffWithinAt, mfld_simps] alias ⟨UniqueMDiffWithinAt.uniqueDiffWithinAt, UniqueDiffWithinAt.uniqueMDiffWithinAt⟩ := uniqueMDiffWithinAt_iff_uniqueDiffWithinAt -theorem uniqueMDiffOn_iff_uniqueDiffOn : UniqueMDiffOn 𝓘(𝕜, E) s ↔ UniqueDiffOn 𝕜 s := by +theorem uniqueMDiffOn_iff_uniqueDiffOn : UniqueMDiff[s] ↔ UniqueDiffOn 𝕜 s := by simp [UniqueMDiffOn, UniqueDiffOn, uniqueMDiffWithinAt_iff_uniqueDiffWithinAt] alias ⟨UniqueMDiffOn.uniqueDiffOn, UniqueDiffOn.uniqueMDiffOn⟩ := uniqueMDiffOn_iff_uniqueDiffOn theorem ModelWithCorners.uniqueMDiffOn {H : Type*} [TopologicalSpace H] - (I : ModelWithCorners 𝕜 E H) : UniqueMDiffOn 𝓘(𝕜, E) (Set.range I) := + (I : ModelWithCorners 𝕜 E H) : UniqueMDiff[Set.range I] := I.uniqueDiffOn.uniqueMDiffOn @[simp, mfld_simps] theorem writtenInExtChartAt_model_space : writtenInExtChartAt 𝓘(𝕜, E) 𝓘(𝕜, E') x f = f := rfl +variable {f' : TangentSpace 𝓘(𝕜, E) x →L[𝕜] TangentSpace 𝓘(𝕜, E') (f x)} + set_option backward.isDefEq.respectTransparency false in -theorem hasMFDerivWithinAt_iff_hasFDerivWithinAt {f'} : - HasMFDerivWithinAt 𝓘(𝕜, E) 𝓘(𝕜, E') f s x f' ↔ HasFDerivWithinAt f f' s x := by +theorem hasMFDerivWithinAt_iff_hasFDerivWithinAt : + HasMFDerivAt[s] f x f' ↔ HasFDerivWithinAt f f' s x := by simpa only [HasMFDerivWithinAt, and_iff_right_iff_imp, mfld_simps] using HasFDerivWithinAt.continuousWithinAt alias ⟨HasMFDerivWithinAt.hasFDerivWithinAt, HasFDerivWithinAt.hasMFDerivWithinAt⟩ := hasMFDerivWithinAt_iff_hasFDerivWithinAt -theorem hasMFDerivAt_iff_hasFDerivAt {f'} : - HasMFDerivAt 𝓘(𝕜, E) 𝓘(𝕜, E') f x f' ↔ HasFDerivAt f f' x := by +theorem hasMFDerivAt_iff_hasFDerivAt : HasMFDerivAt% f x f' ↔ HasFDerivAt f f' x := by rw [← hasMFDerivWithinAt_univ, hasMFDerivWithinAt_iff_hasFDerivWithinAt, hasFDerivWithinAt_univ] alias ⟨HasMFDerivAt.hasFDerivAt, HasFDerivAt.hasMFDerivAt⟩ := hasMFDerivAt_iff_hasFDerivAt diff --git a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean index 0cc8f598e3708b..bbf7333557df2e 100644 --- a/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean +++ b/Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean @@ -9,22 +9,34 @@ public import Mathlib.Geometry.Manifold.Algebra.SMul public import Mathlib.Geometry.Manifold.ContMDiff.NormedSpace public import Mathlib.Geometry.Manifold.MFDeriv.SpecificFunctions -/-! ## Equivalence of manifold differentiability with the basic definition for functions between +/-! # Equivalence of manifold differentiability with the basic definition for functions between vector spaces The API in this file is mostly copied from `Mathlib/Geometry/Manifold/ContMDiff/NormedSpace.lean`, providing the same statements for higher smoothness. In this file, we do the same for differentiability. -In addition to the above, this file provides -* results about the differentiability of scalar multiplication (`mfderiv_smul` and friends), -* `mvfderiv`: the exterior derivative of a vector-valued function, as a section of the - cotangent bundle; adds notation `d% f` for `mvfderiv I f` via a custom elaborator scoped to the - `Manifold` namespace, with a corresponding delaborator, and - adds basic lemmas about `mvfderiv` (such as addition, subtraction, multiplication and constants). -* `mvfderivWithin` with notation `d[s]f` for `mvfderivWithin I f s` in the `Manifold` namespace: +## Main definitions + +In addition to the above, this file provides two important definitions. +* `mvfderiv I f x` is the manifold Fréchet derivative at `x : M` of a vector-valued function + `f : M → V`, but taking values in the target normed space `V` instead of `TangentSpace% (f x) V`. + Mathematically, this uses the global trivialization `T V ≅ V × V`, yielding an identification + `T_v V ≅ V` for each `v : V`. In Lean, we post-compose the differential `mfderiv% f x` with + `NormedSpace.fromTangentSpace`. If `V` is a field, this coincides with the exterior derivative + of `f` as a section of the cotangent bundle. + There is notation `d% f` for `mvfderiv I f` via a custom elaborator scoped to the + `Manifold` namespace, with a corresponding delaborator, +* `mvfderivWithin` with notation `d[s] f` for `mvfderivWithin I f s` in the `Manifold` namespace: the analogous concept within a set, with analogous API lemmas +## Main results + +This file contains +* results about the differentiability of scalar multiplication (`mfderiv_smul` and friends), +* basic lemmas about `mvfderiv` (such as addition, subtraction, multiplication and constants), +* analogous lemmas about `mvfderivWithin`. + -/ public section @@ -298,15 +310,18 @@ theorem MDifferentiableWithinAt.smul ((contMDiff_smul.of_le le_top).mdifferentiable one_ne_zero _).comp_mdifferentiableWithinAt x (hf.prodMk hg) +@[to_fun] theorem MDifferentiableAt.smul (hf : MDiffAt f x) - (hg : MDiffAt g x) : MDiffAt (fun p ↦ f p • g p) x := + (hg : MDiffAt g x) : MDiffAt (f • g) x := ((contMDiff_smul.of_le le_top).mdifferentiable one_ne_zero _).comp x (hf.prodMk hg) +@[to_fun] theorem MDifferentiableOn.smul (hf : MDiff[s] f) - (hg : MDiff[s] g) : MDiff[s] (fun p ↦ f p • g p) := + (hg : MDiff[s] g) : MDiff[s] (f • g) := fun x hx ↦ (hf x hx).smul (hg x hx) -theorem MDifferentiable.smul (hf : MDiff f) (hg : MDiff g) : MDiff fun p ↦ f p • g p := +@[to_fun] +theorem MDifferentiable.smul (hf : MDiff f) (hg : MDiff g) : MDiff (f • g) := fun x ↦ (hf x).smul (hg x) -- TODO: deprecate in favour of `mvfderiv_smul`, then delete this lemma @@ -410,8 +425,10 @@ end smul /-! ### Exterior derivative of a vector-valued function -/ variable (I) in -/-- `mvfderiv I J f x` is the exterior derivative of a vector-valued function `g` on `M`, -as a section of the cotangent bundle. +/-- `mvfderivWithin I J f s x` is the `mfderiv` of a vector-valued function `f` on `M` at `x` +within the set `s`, but taking values in the target normed space directly. +The difference to `mfderivWithin` is explained in the module-docstring for +`Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean`. Future: this could be generalised to functions into additive torsors over abelian Lie groups. -/ @@ -421,8 +438,10 @@ noncomputable def mvfderivWithin (g : M → F) (s : Set M) : fun x ↦ (NormedSpace.fromTangentSpace <| g x).toContinuousLinearMap ∘L (mfderiv[s] g x) variable (I) in -/-- The exterior derivative of a vector-valued function on `M`, -as a section of the cotangent bundle. +/-- `mvfderiv I J f x` is the `mfderiv` of a vector-valued function `f` on `M` at `x`, +but taking values in the target normed space directly. +The difference to `mfderiv` is explained in the module-docstring for +`Mathlib/Geometry/Manifold/MFDeriv/NormedSpace.lean`. Future: this could be generalised to functions into additive torsors over abelian Lie groups. -/ @@ -486,27 +505,27 @@ lemma mvfderivWithin_const (c : F) {x : M} : d[s] (fun _ : M ↦ c) x = 0 := by @[simp, to_fun mvfderivWithin_fun_add] lemma mvfderivWithin_add {g g' : M → F} {x : M} - (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffWithinAt I s x) : + (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffAt[s] x) : d[s](g + g') x = d[s]g x + d[s]g' x := by simp [mvfderivWithin, mfderivWithin_add hg hg' hs] rfl @[simp, to_fun mvfderivWithin_fun_sub] lemma mvfderivWithin_sub {g g' : M → F} {x : M} - (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffWithinAt I s x) : + (hg : MDiffAt[s] g x) (hg' : MDiffAt[s] g' x) (hs : UniqueMDiffAt[s] x) : d[s](g - g') x = d[s]g x - d[s]g' x := by simp [mvfderivWithin, mfderivWithin_sub hg hg' hs] rfl @[simp, to_fun mvfderivWithin_fun_neg] -lemma mvfderivWithin_neg {g : M → F} {x : M} (hs : UniqueMDiffWithinAt I s x) : +lemma mvfderivWithin_neg {g : M → F} {x : M} (hs : UniqueMDiffAt[s] x) : d[s](-g) x = -d[s]g x := by simp [mvfderivWithin, mfderivWithin_neg hs] rfl @[simp, to_fun mvfderivWithin_fun_smul] lemma mvfderivWithin_smul {a : M → 𝕜} (ha : MDiffAt[s] a x) {g : M → F} (hg : MDiffAt[s] g x) - (hs : UniqueMDiffWithinAt I s x) : + (hs : UniqueMDiffAt[s] x) : d[s](a • g) x = a x • d[s] g x + (d[s] a x).smulRight (g x) := by refine HasMFDerivWithinAt.mfderivWithin ⟨ha.1.smul hg.1, ?_⟩ hs @@ -516,14 +535,14 @@ lemma mvfderivWithin_smul {a : M → 𝕜} (ha : MDiffAt[s] a x) {g : M → F} ( @[simp, to_fun mvfderivWithin_fun_mul] lemma mvfderivWithin_mul {f g : M → 𝕜} {x : M} (hf : MDiffAt[s] f x) (hg : MDiffAt[s] g x) - (hs : UniqueMDiffWithinAt I s x) : + (hs : UniqueMDiffAt[s] x) : d[s](f * g) x = f x • d[s]g x + (g x) • (d[s]f x) := by convert! mvfderivWithin_smul hf hg hs ext v simp [mul_comm] @[simp] -lemma mvfderivWithin_zero {s : Set M} (hs : UniqueMDiffWithinAt I s x) : +lemma mvfderivWithin_zero {s : Set M} (hs : UniqueMDiffAt[s] x) : d[s] (0 : M → F) x = 0 := by have : d[s] (0 : M → F) x + d[s] (0 : M → F) x = d[s] (0 : M → F) x := by rw [← mvfderivWithin_add (by exact mdifferentiableWithinAt_const) diff --git a/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean b/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean index 60b917645a0878..1ab6f3612e7c95 100644 --- a/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean +++ b/Mathlib/Geometry/Manifold/MFDeriv/SpecificFunctions.lean @@ -83,7 +83,7 @@ protected theorem mdifferentiable : MDiff f := theorem mfderiv_eq : mfderiv% f x = f := f.hasMFDerivAt.mfderiv -theorem mfderivWithin_eq (hs : UniqueMDiffWithinAt 𝓘(𝕜, E) s x) : mfderiv[s] f x = f := +theorem mfderivWithin_eq (hs : UniqueMDiffAt[s] x) : mfderiv[s] f x = f := f.hasMFDerivWithinAt.mfderivWithin hs end ContinuousLinearMap @@ -113,7 +113,7 @@ protected theorem mdifferentiable : MDiff f := theorem mfderiv_eq : mfderiv% f x = (f : E →L[𝕜] E') := f.hasMFDerivAt.mfderiv -theorem mfderivWithin_eq (hs : UniqueMDiffWithinAt 𝓘(𝕜, E) s x) : +theorem mfderivWithin_eq (hs : UniqueMDiffAt[s] x) : mfderiv[s] f x = (f : E →L[𝕜] E') := f.hasMFDerivWithinAt.mfderivWithin hs @@ -153,7 +153,7 @@ theorem mdifferentiableOn_id : MDiff[s] (@id M) := theorem mfderiv_id : mfderiv% (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace% x) := (hasMFDerivAt_id x).mfderiv -theorem mfderivWithin_id (hxs : UniqueMDiffWithinAt I s x) : +theorem mfderivWithin_id (hxs : UniqueMDiffAt[s] x) : mfderiv[s] (@id M) x = ContinuousLinearMap.id 𝕜 (TangentSpace% x) := by rw [MDifferentiable.mfderivWithin mdifferentiableAt_id hxs] exact mfderiv_id @@ -162,7 +162,7 @@ set_option backward.isDefEq.respectTransparency false in @[simp, mfld_simps] theorem tangentMap_id : tangentMap% (@id M) = id := by ext1 ⟨x, v⟩; simp [tangentMap] -theorem tangentMapWithin_id {p : TangentBundle I M} (hs : UniqueMDiffWithinAt I s p.proj) : +theorem tangentMapWithin_id {p : TangentBundle I M} (hs : UniqueMDiffAt[s] p.proj) : tangentMap[s] (id : M → M) p = p := by simp only [tangentMapWithin, id] rw [mfderivWithin_id] @@ -227,7 +227,7 @@ theorem HasMFDerivWithinAt.prodMk {f : M → M'} {g : M → M''} ⟨hf.1.prodMk hg.1, hf.2.prodMk hg.2⟩ lemma mfderivWithin_prodMk {f : M → M'} {g : M → M''} (hf : MDiffAt[s] f x) (hg : MDiffAt[s] g x) - (hs : UniqueMDiffWithinAt I s x) : + (hs : UniqueMDiffAt[s] x) : mfderiv[s] (fun x ↦ (f x, g x)) x = (mfderiv[s] f x).prod (mfderiv[s] g x) := (hf.hasMFDerivWithinAt.prodMk hg.hasMFDerivWithinAt).mfderivWithin hs @@ -316,7 +316,7 @@ theorem mfderiv_fst {x : M × M'} : (hasMFDerivAt_fst x).mfderiv theorem mfderivWithin_fst {s : Set (M × M')} {x : M × M'} - (hxs : UniqueMDiffWithinAt (I.prod I') s x) : + (hxs : UniqueMDiffAt[s] x) : mfderiv[s] (@Prod.fst M M') x = ContinuousLinearMap.fst 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) := by rw [MDifferentiable.mfderivWithin mdifferentiableAt_fst hxs]; exact mfderiv_fst @@ -327,7 +327,7 @@ theorem tangentMap_prodFst {p : TangentBundle (I.prod I') (M × M')} : simp [tangentMap]; rfl theorem tangentMapWithin_prodFst {s : Set (M × M')} {p : TangentBundle (I.prod I') (M × M')} - (hs : UniqueMDiffWithinAt (I.prod I') s p.proj) : + (hs : UniqueMDiffAt[s] p.proj) : tangentMap[s] (@Prod.fst M M') p = ⟨p.proj.1, p.2.1⟩ := by simp only [tangentMapWithin] rw [mfderivWithin_fst] @@ -375,7 +375,7 @@ theorem mfderiv_snd {x : M × M'} : (hasMFDerivAt_snd x).mfderiv theorem mfderivWithin_snd {s : Set (M × M')} {x : M × M'} - (hxs : UniqueMDiffWithinAt (I.prod I') s x) : + (hxs : UniqueMDiffAt[s] x) : mfderiv[s] (@Prod.snd M M') x = ContinuousLinearMap.snd 𝕜 (TangentSpace% x.1) (TangentSpace% x.2) := by rw [MDifferentiable.mfderivWithin mdifferentiableAt_snd hxs]; exact mfderiv_snd @@ -516,7 +516,7 @@ lemma HasMFDerivAt.prodMap {p : M × M'} {f : M → N} {g : M' → N'} -- could be a strict superset of `s`. lemma mfderivWithin_prodMap {p : M × M'} {t : Set M'} {f : M → N} {g : M' → N'} (hf : MDiffAt[s] f p.1) (hg : MDiffAt[t] g p.2) - (hs : UniqueMDiffWithinAt I s p.1) (ht : UniqueMDiffWithinAt I' t p.2) : + (hs : UniqueMDiffAt[s] p.1) (ht : UniqueMDiffAt[t] p.2) : mfderiv[s ×ˢ t] (Prod.map f g) p = (mfderiv[s] f p.1).prodMap (mfderiv[t] g p.2) := by have hf' : HasMFDerivAt[Prod.fst '' s ×ˢ t] f p.1 (mfderiv[s] f p.1) := hf.hasMFDerivWithinAt.mono (by grind) @@ -539,7 +539,7 @@ theorem tangentMap_prodSnd {p : TangentBundle (I.prod I') (M × M')} : simp [tangentMap]; rfl theorem tangentMapWithin_prodSnd {s : Set (M × M')} {p : TangentBundle (I.prod I') (M × M')} - (hs : UniqueMDiffWithinAt (I.prod I') s p.proj) : + (hs : UniqueMDiffAt[s] p.proj) : tangentMap[s] (@Prod.snd M M') p = ⟨p.proj.2, p.2.2⟩ := by simp only [tangentMapWithin] rw [mfderivWithin_snd hs] @@ -671,7 +671,7 @@ theorem hasMFDerivAt_sumSwap : cases p <;> simp @[simp] -theorem mfderivWithin_sumSwap {s : Set (M ⊕ M')} (hs : UniqueMDiffWithinAt I s p) : +theorem mfderivWithin_sumSwap {s : Set (M ⊕ M')} (hs : UniqueMDiffAt[s] p) : mfderiv[s] (@Sum.swap M M') p = ContinuousLinearMap.id 𝕜 (TangentSpace% p) := hasMFDerivAt_sumSwap.hasMFDerivWithinAt.mfderivWithin hs @@ -732,7 +732,7 @@ theorem hasMFDerivAt_inr : HasMFDerivAt% (@Sum.inr M M') q' (ContinuousLinearMap.id 𝕜 (TangentSpace% p)) := by simpa [HasMFDerivAt, hasMFDerivWithinAt_univ] using! hasMFDerivWithinAt_inr (t := Set.univ) -theorem mfderivWithin_sumInl (hU : UniqueMDiffWithinAt I s q) : +theorem mfderivWithin_sumInl (hU : UniqueMDiffAt[s] q) : mfderiv[s] (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace% p) := hasMFDerivWithinAt_inl.mfderivWithin hU @@ -740,7 +740,7 @@ theorem mfderiv_sumInl : mfderiv% (@Sum.inl M M') q = ContinuousLinearMap.id 𝕜 (TangentSpace% p) := by simpa [mfderivWithin_univ] using (mfderivWithin_sumInl (uniqueMDiffWithinAt_univ I)) -theorem mfderivWithin_sumInr {t : Set M'} (hU : UniqueMDiffWithinAt I t q') : +theorem mfderivWithin_sumInr {t : Set M'} (hU : UniqueMDiffAt[t] q') : mfderiv[t] (@Sum.inr M M') q' = ContinuousLinearMap.id 𝕜 (TangentSpace% q') := hasMFDerivWithinAt_inr.mfderivWithin hU @@ -794,7 +794,7 @@ theorem mfderiv_add (hf : MDiffAt f z) (hg : MDiffAt g z) : (hf.hasMFDerivAt.add hg.hasMFDerivAt).mfderiv theorem mfderivWithin_add (hf : MDiffAt[s] f z) (hg : MDiffAt[s] g z) - (hs : UniqueMDiffWithinAt I s z) : + (hs : UniqueMDiffAt[s] z) : (mfderiv[s] (f + g) z : TangentSpace% z →L[𝕜] E') = (by exact mfderiv[s] f z) + (by exact mfderiv[s] g z) := (hf.hasMFDerivWithinAt.add hg.hasMFDerivWithinAt).mfderivWithin hs @@ -881,7 +881,7 @@ theorem mdifferentiableAt_neg : MDiffAt (-f) z ↔ MDiffAt f z := theorem MDifferentiable.neg (hf : MDiff f) : MDiff (-f) := fun x ↦ (hf x).neg set_option backward.isDefEq.respectTransparency false in -theorem mfderivWithin_neg (hs : UniqueMDiffWithinAt I s x) : +theorem mfderivWithin_neg (hs : UniqueMDiffAt[s] x) : mfderiv[s] (-f) x = -mfderiv[s] f x := by simp_rw [mfderivWithin] by_cases hf : MDiffAt[s] f x @@ -914,7 +914,7 @@ theorem MDifferentiable.sub (hf : MDiff f) (hg : MDiff g) : MDiff (f - g) := fun x ↦ (hf x).sub (hg x) theorem mfderivWithin_sub (hf : MDiffAt[s] f z) (hg : MDiffAt[s] g z) - (hs : UniqueMDiffWithinAt I s z) : + (hs : UniqueMDiffAt[s] z) : (mfderiv[s] (f - g) z : TangentSpace% z →L[𝕜] E') = (by exact mfderiv[s] f z) - (by exact mfderiv[s] g z) := (hf.hasMFDerivWithinAt.sub hg.hasMFDerivWithinAt).mfderivWithin hs diff --git a/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean b/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean index 87483b86150d63..f85c3efacc4755 100644 --- a/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean +++ b/Mathlib/Geometry/Manifold/MFDeriv/UniqueDifferential.lean @@ -45,9 +45,9 @@ section /-- If `s` has the unique differential property at `x`, `f` is differentiable within `s` at `x` and its derivative has dense range, then `f '' s` has the unique differential property at `f x`. -/ -theorem UniqueMDiffWithinAt.image_denseRange (hs : UniqueMDiffWithinAt I s x) +theorem UniqueMDiffWithinAt.image_denseRange (hs : UniqueMDiffAt[s] x) {f : M → M'} {f' : E →L[𝕜] E'} (hf : HasMFDerivAt[s] f x f') - (hd : DenseRange f') : UniqueMDiffWithinAt I' (f '' s) (f x) := by + (hd : DenseRange f') : UniqueMDiffAt[f '' s] (f x) := by /- Rewrite in coordinates, apply `HasFDerivWithinAt.uniqueDiffWithinAt`. -/ have := hs.inter' <| hf.1 (extChartAt_source_mem_nhds (I := I') (f x)) refine (((hf.2.mono ?sub1).uniqueDiffWithinAt this hd).mono ?sub2).congr_pt ?pt @@ -60,22 +60,22 @@ theorem UniqueMDiffWithinAt.image_denseRange (hs : UniqueMDiffWithinAt I s x) /-- If `s` has the unique differential property, `f` is differentiable on `s` and its derivative at every point of `s` has dense range, then `f '' s` has the unique differential property. This version uses the `HasMFDerivWithinAt` predicate. -/ -theorem UniqueMDiffOn.image_denseRange' (hs : UniqueMDiffOn I s) {f : M → M'} +theorem UniqueMDiffOn.image_denseRange' (hs : UniqueMDiff[s]) {f : M → M'} {f' : M → E →L[𝕜] E'} (hf : ∀ x ∈ s, HasMFDerivAt[s] f x (f' x)) (hd : ∀ x ∈ s, DenseRange (f' x)) : - UniqueMDiffOn I' (f '' s) := + UniqueMDiff[f '' s] := forall_mem_image.2 fun x hx ↦ (hs x hx).image_denseRange (hf x hx) (hd x hx) /-- If `s` has the unique differential property, `f` is differentiable on `s` and its derivative at every point of `s` has dense range, then `f '' s` has the unique differential property. -/ -theorem UniqueMDiffOn.image_denseRange (hs : UniqueMDiffOn I s) {f : M → M'} +theorem UniqueMDiffOn.image_denseRange (hs : UniqueMDiff[s]) {f : M → M'} (hf : MDiff[s] f) (hd : ∀ x ∈ s, DenseRange (mfderiv[s] f x)) : - UniqueMDiffOn I' (f '' s) := + UniqueMDiff[f '' s] := hs.image_denseRange' (fun x hx ↦ (hf x hx).hasMFDerivWithinAt) hd protected theorem UniqueMDiffWithinAt.preimage_openPartialHomeomorph - (hs : UniqueMDiffWithinAt I s x) {e : OpenPartialHomeomorph M M'} (he : e.MDifferentiable I I') - (hx : x ∈ e.source) : UniqueMDiffWithinAt I' (e.target ∩ e.symm ⁻¹' s) (e x) := by + (hs : UniqueMDiffAt[s] x) {e : OpenPartialHomeomorph M M'} (he : e.MDifferentiable I I') + (hx : x ∈ e.source) : UniqueMDiffAt[e.target ∩ e.symm ⁻¹' s] (e x) := by rw [← e.image_source_inter_eq', inter_comm] exact (hs.inter (e.open_source.mem_nhds hx)).image_denseRange (he.mdifferentiableAt hx).hasMFDerivAt.hasMFDerivWithinAt @@ -83,16 +83,16 @@ protected theorem UniqueMDiffWithinAt.preimage_openPartialHomeomorph /-- If a set has the unique differential property, then its image under a local diffeomorphism also has the unique differential property. -/ -theorem UniqueMDiffOn.uniqueMDiffOn_preimage (hs : UniqueMDiffOn I s) +theorem UniqueMDiffOn.uniqueMDiffOn_preimage (hs : UniqueMDiff[s]) {e : OpenPartialHomeomorph M M'} (he : e.MDifferentiable I I') : - UniqueMDiffOn I' (e.target ∩ e.symm ⁻¹' s) := fun _x hx ↦ + UniqueMDiff[e.target ∩ e.symm ⁻¹' s] := fun _x hx ↦ e.right_inv hx.1 ▸ (hs _ hx.2).preimage_openPartialHomeomorph he (e.map_target hx.1) variable [IsManifold I 1 M] in /-- If a set in a manifold has the unique derivative property, then its pullback by any extended chart, in the vector space, also has the unique derivative property. -/ -theorem UniqueMDiffOn.uniqueMDiffOn_target_inter (hs : UniqueMDiffOn I s) (x : M) : - UniqueMDiffOn 𝓘(𝕜, E) ((extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s) := by +theorem UniqueMDiffOn.uniqueMDiffOn_target_inter (hs : UniqueMDiff[s]) (x : M) : + UniqueMDiff[(extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s] := by -- this is just a reformulation of `UniqueMDiffOn.uniqueMDiffOn_preimage`, using as `e` -- the local chart at `x`. rw [← PartialEquiv.image_source_inter_eq', inter_comm, extChartAt_source] @@ -103,12 +103,12 @@ theorem UniqueMDiffOn.uniqueMDiffOn_target_inter (hs : UniqueMDiffOn I s) (x : M variable [IsManifold I 1 M] in /-- If a set in a manifold has the unique derivative property, then its pullback by any extended chart, in the vector space, also has the unique derivative property. -/ -theorem UniqueMDiffOn.uniqueDiffOn_target_inter (hs : UniqueMDiffOn I s) (x : M) : +theorem UniqueMDiffOn.uniqueDiffOn_target_inter (hs : UniqueMDiff[s]) (x : M) : UniqueDiffOn 𝕜 ((extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s) := (hs.uniqueMDiffOn_target_inter x).uniqueDiffOn variable [IsManifold I 1 M] in -theorem UniqueMDiffOn.uniqueDiffWithinAt_range_inter (hs : UniqueMDiffOn I s) (x : M) (y : E) +theorem UniqueMDiffOn.uniqueDiffWithinAt_range_inter (hs : UniqueMDiff[s]) (x : M) (y : E) (hy : y ∈ (extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s) : UniqueDiffWithinAt 𝕜 (range I ∩ (extChartAt I x).symm ⁻¹' s) y := by apply (hs.uniqueDiffOn_target_inter x y hy).mono @@ -118,11 +118,11 @@ variable [IsManifold I 1 M] in /-- When considering functions between manifolds, this statement shows up often. It entails the unique differential of the pullback in extended charts of the set where the function can be read in the charts. -/ -theorem UniqueMDiffOn.uniqueDiffOn_inter_preimage (hs : UniqueMDiffOn I s) (x : M) (y : M'') +theorem UniqueMDiffOn.uniqueDiffOn_inter_preimage (hs : UniqueMDiff[s]) (x : M) (y : M'') {f : M → M''} (hf : ContinuousOn f s) : UniqueDiffOn 𝕜 ((extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' (s ∩ f ⁻¹' (extChartAt I' y).source)) := - haveI : UniqueMDiffOn I (s ∩ f ⁻¹' (extChartAt I' y).source) := by + haveI : UniqueMDiff[s ∩ f ⁻¹' (extChartAt I' y).source] := by intro z hz apply (hs z hz.1).inter' apply (hf z hz.1).preimage_mem_nhdsWithin @@ -138,8 +138,8 @@ variable {F : Type*} [NormedAddCommGroup F] [NormedSpace 𝕜 F] {Z : M → Type set_option backward.isDefEq.respectTransparency false in private lemma UniqueMDiffWithinAt.bundle_preimage_aux {p : TotalSpace F Z} - (hs : UniqueMDiffWithinAt I s p.proj) (h's : s ⊆ (trivializationAt F Z p.proj).baseSet) : - UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) p := by + (hs : UniqueMDiffAt[s] p.proj) (h's : s ⊆ (trivializationAt F Z p.proj).baseSet) : + UniqueMDiffAt[π F Z ⁻¹' s] p := by suffices ((extChartAt I p.proj).symm ⁻¹' s ∩ range I) ×ˢ univ ⊆ (extChartAt (I.prod 𝓘(𝕜, F)) p).symm ⁻¹' (TotalSpace.proj ⁻¹' s) ∩ range (I.prod 𝓘(𝕜, F)) by let w := (extChartAt (I.prod 𝓘(𝕜, F)) p p).2 @@ -169,27 +169,25 @@ private lemma UniqueMDiffWithinAt.bundle_preimage_aux {p : TotalSpace F Z} /-- In a fiber bundle, the preimage under the projection of a set with unique differentials in the base has unique differentials in the bundle. -/ -theorem UniqueMDiffWithinAt.bundle_preimage {p : TotalSpace F Z} - (hs : UniqueMDiffWithinAt I s p.proj) : - UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) p := by - suffices UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) - (π F Z ⁻¹' (s ∩ (trivializationAt F Z p.proj).baseSet)) p from this.mono (by simp) +theorem UniqueMDiffWithinAt.bundle_preimage {p : TotalSpace F Z} (hs : UniqueMDiffAt[s] p.proj) : + UniqueMDiffAt[π F Z ⁻¹' s] p := by + suffices UniqueMDiffAt[π F Z ⁻¹' (s ∩ (trivializationAt F Z p.proj).baseSet)] p from + this.mono (by simp) apply UniqueMDiffWithinAt.bundle_preimage_aux (hs.inter _) inter_subset_right - exact IsOpen.mem_nhds (trivializationAt F Z p.proj).open_baseSet + exact (trivializationAt F Z p.proj).open_baseSet.mem_nhds (FiberBundle.mem_baseSet_trivializationAt' p.proj) variable (Z) /-- In a fiber bundle, the preimage under the projection of a set with unique differentials in the base has unique differentials in the bundle. Version with a point `⟨b, x⟩`. -/ -theorem UniqueMDiffWithinAt.bundle_preimage' {b : M} (hs : UniqueMDiffWithinAt I s b) - (x : Z b) : UniqueMDiffWithinAt (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) ⟨b, x⟩ := +theorem UniqueMDiffWithinAt.bundle_preimage' {b : M} (hs : UniqueMDiffAt[s] b) (x : Z b) : + UniqueMDiffAt[π F Z ⁻¹' s] ⟨b, x⟩ := hs.bundle_preimage (p := ⟨b, x⟩) /-- In a fiber bundle, the preimage under the projection of a set with unique differentials in the base has unique differentials in the bundle. -/ -theorem UniqueMDiffOn.bundle_preimage (hs : UniqueMDiffOn I s) : - UniqueMDiffOn (I.prod 𝓘(𝕜, F)) (π F Z ⁻¹' s) := fun _p hp ↦ - (hs _ hp).bundle_preimage +theorem UniqueMDiffOn.bundle_preimage (hs : UniqueMDiff[s]) : UniqueMDiff[π F Z ⁻¹' s] := + fun _p hp ↦ (hs _ hp).bundle_preimage end UniqueMDiff diff --git a/Mathlib/Geometry/Manifold/Notation.lean b/Mathlib/Geometry/Manifold/Notation.lean index 24e6737558c2c5..a3d519596a8156 100644 --- a/Mathlib/Geometry/Manifold/Notation.lean +++ b/Mathlib/Geometry/Manifold/Notation.lean @@ -40,6 +40,8 @@ including inference of the model with corners. | `TangentSpace% x` | `TangentSpace I x` | | `tangentMap[s] f` | `tangentMapWithin I J f s` | | `tangentMap% f` | `tangentMap I J f` | +| `UniqueMDiff[s]` | `UniqueMDiffOn I s` | +| `UniqueMDiffAt[s] x` | `UniqueMDiffWithinAt I s x` | In each of these cases, the models with corners are inferred from the domain and codomain of `f`. The search for models with corners uses the local context and is (almost) only based on expression @@ -329,7 +331,29 @@ private def tryStrategy (strategyDescr : MessageData) (x : TermElabM FindModelRe s.restore true return none -set_option linter.style.emptyLine false in -- linter false positive +/-- Given an `Expr`ession `e`, try to find a `NormedSpace` instance on `e` and return the +underlying base field. Search local instances, before recursing into product and bundled +continuous linear maps. -/ +partial def guessBaseFieldForNormedSpace (e : Expr) : TermElabM <| Option Expr := do + if let some k ← findFromLocalInstance e then return k + match_expr e with + | Prod E _F => + guessBaseFieldForNormedSpace E + | _ => + try + let (_k, E, _F) ← isCLMReduciblyDefeqCoefficients e + guessBaseFieldForNormedSpace E + catch _e => + findFromLocalInstance e +where findFromLocalInstance (e : Expr) : TermElabM <| Option Expr := do + findSomeLocalInstanceOf? ``NormedSpace fun _ type ↦ do + match_expr type with + | NormedSpace K E _ _ => + if ← withReducible (pureIsDefEq E e) then + trace[Elab.DiffGeo.MDiff] "`{e}` is a normed field over `{K}`"; return some K + else return none + | _ => pure none + /-- Try to find a `ModelWithCorners` instance on a type (represented by an expression `e`), using the local context to infer the appropriate instance. This supports the following cases: - the model with corners on the total space of a vector bundle @@ -353,11 +377,6 @@ Return an expression describing the found model with corners, together with info whether the model is the trivial model with corners on a normed space. (This is important for forming products of models.) -`baseInfo` is only used for the first case, a model with corners on the total space of the vector -bundle. In this case, it contains a pair of expressions `(e, i)` describing the type of the base -and the model with corners on the base: these are required to construct the right model with -corners. - Note that the matching on `e` does not see through reducibility (e.g. we distinguish the `abbrev` `TangentBundle` from its definition), so `whnfR` should not be run on `e` prior to calling `findModel` on it. @@ -366,8 +385,7 @@ This implementation is not maximally robust yet. -/ -- TODO: better error messages when all strategies fail -- TODO: consider lowering monad to `MetaM` -def findModelInner (e : Expr) (baseInfo : Option (Expr × Expr) := none) : - TermElabM (Option FindModelResult) := do +partial def findModelInner (e : Expr) : TermElabM (Option FindModelResult) := do if let some m ← tryStrategy "TotalSpace" fromTotalSpace then return some m if let some m ← tryStrategy "TangentBundle" fromTangentBundle then return some m if let some m ← tryStrategy "NormedSpace" fromNormedSpace then return some m @@ -387,51 +405,49 @@ def findModelInner (e : Expr) (baseInfo : Option (Expr × Expr) := none) : where /- Note that errors thrown in the following are caught by `tryStrategy` and converted to trace messages. -/ - /-- Attempt to find a model from a `TotalSpace` first by attempting to use any provided - `baseInfo`, then by seeing if it is the total space of a tangent bundle. -/ + /-- Attempt to find a model from a `TotalSpace` first by seeing if it is the total space of a + tangent bundle, and otherwise by finding a model with corners on its base. -/ fromTotalSpace : TermElabM FindModelResult := do match_expr e with | Bundle.TotalSpace _ F V => do - if let some m ← tryStrategy m!"From base info" (fromTotalSpace.fromBaseInfo F) then return m if let some m ← tryStrategy m!"TangentSpace" (fromTotalSpace.tangentSpace V) then return m - throwError "Having a TotalSpace as source is not yet supported" + trace[Elab.DiffGeo.MDiff] + "{e} is the total space of a fiber bundle: trying to find a model on the base of `{V}`" + -- `V` should be of type `B → Type*`, where `B` is the base of the vector bundle. + -- Then, the desired model with corners is `I.prod (𝓘(𝕜, F))`, where `I` is the model on `B` + -- and `𝕜` is the base field for `F`. + let vtype ← whnf <| ← instantiateMVars <| ← inferType V + trace[Elab.DiffGeo.MDiff] "`{V}` has type `{vtype}`" + match vtype with + | .forallE _x base _tgt _ => + let baseModel ← withTraceNode `Elab.DiffGeo.MDiff + (fun _ ↦ pure m!"searching for a model with corners on the base `{base}`") do + let some baseI ← findModelInner base + | throwError m!"found no model with corners on the base {base} of `TotalSpace {F} {V}`" + return baseI.model + -- Very likely, `F` is a normed space over some field: let's see if `F` is a normed space + -- on the nose. + let some K ← guessBaseFieldForNormedSpace F + | throwError "Couldn't find a `NormedSpace` structure on `{F}`" + let tgtMod ← mkAppOptM ``modelWithCornersSelf #[K, none, F, none, none] + mkAppM ``ModelWithCorners.prod #[baseModel, tgtMod] + | _ => + throwError s!"{e} is a TotalSpace {F} {V}, but {V} is not a pi type --- \ + could not infer base of the bundle" | _ => throwError "`{e}` is not a `Bundle.TotalSpace`." - /-- Attempt to use the provided `baseInfo` to find a model. -/ - fromTotalSpace.fromBaseInfo (F : Expr) : TermElabM Expr := do - if let some (src, srcI) := baseInfo then - trace[Elab.DiffGeo.MDiff] "Using base info `{src}`, `{srcI}`" - let some K ← findSomeLocalInstanceOf? ``NormedSpace fun _ type ↦ do - match_expr type with - | NormedSpace K E _ _ => - if ← withReducible (pureIsDefEq E F) then - trace[Elab.DiffGeo.MDiff] "`{F}` is a normed field over `{K}`"; return some K - else return none - | _ => return none - | throwError "Couldn't find a `NormedSpace` structure on `{F}` among local instances." - let kT : Term ← Term.exprToSyntax K - let srcIT : Term ← Term.exprToSyntax srcI - let FT : Term ← Term.exprToSyntax F - let iTerm : Term ← ``(ModelWithCorners.prod $srcIT 𝓘($kT, $FT)) - Term.elabTerm iTerm none - else - throwError "No `baseInfo` provided" /-- Attempt to find a model from the total space of a tangent bundle. -/ fromTotalSpace.tangentSpace (V : Expr) : TermElabM Expr := do match_expr V with | TangentSpace _k _ _E _ _ _H _ I M _ _ => do trace[Elab.DiffGeo.MDiff] "`{V}` is the total space of the `TangentBundle` of `{M}`" - let srcIT : Term ← Term.exprToSyntax I - let resTerm : Term ← ``(ModelWithCorners.prod $srcIT (ModelWithCorners.tangent $srcIT)) - Term.elabTerm resTerm none + mkAppM ``ModelWithCorners.tangent #[I] | _ => throwError "`{V}` is not a `TangentSpace`" /-- Attempt to find a model on a `TangentBundle` -/ fromTangentBundle : TermElabM Expr := do match_expr e with | TangentBundle _k _ _E _ _ _H _ I M _ _ => do trace[Elab.DiffGeo.MDiff] "`{e}` is a `TangentBundle` over model `{I}` on `{M}`" - let srcIT : Term ← Term.exprToSyntax I - let resTerm : Term ← ``(ModelWithCorners.tangent $srcIT) - Term.elabTerm resTerm none + mkAppM ``ModelWithCorners.tangent #[I] | _ => throwError "`{e}` is not a `TangentBundle`" /-- Attempt to find the trivial model on a normed space. -/ fromNormedSpace : TermElabM FindModelResult := do @@ -474,8 +490,8 @@ where | ChartedSpace H _ M _ => if ← withReducible (pureIsDefEq M e) then trace[Elab.DiffGeo.MDiff] "`{e}` is a charted space over `{H}` via `{inst}`" - return some H else - if ← withReducible (pureIsDefEq H e) then + return some H + else if ← withReducible (pureIsDefEq H e) then trace[Elab.DiffGeo.MDiff] "`{e}` is the charted space of `{M}` via `{inst}`" return some H else return none | _ => return none @@ -486,7 +502,24 @@ where | ModelWithCorners _ _ _ _ _ H' _ => do if ← withReducible (pureIsDefEq H' H) then return some fvar else return none | _ => return none - | throwError "Couldn't find a `ModelWithCorners` with model space `{H}` in the local context." + | trace[Elab.DiffGeo.MDiff] + "Couldn't find a `ModelWithCorners` with model space `{H}` in the local context." + -- Try a normed space, and a normed field as last alternatives. + let a ← findSomeLocalInstanceOf? ``NormedSpace fun inst type ↦ do + match_expr type with + | NormedSpace K E _ _ => + if ← withReducible (pureIsDefEq E H) then return some (inst, K) + else return none + | _ => return none + if let some (inst, K) := a then + trace[Elab.DiffGeo.MDiff] "`{H}` is a normed space over the field `{K}`" + return ← mkAppOptM ``modelWithCornersSelf #[K, none, H, none, inst] + trace[Elab.DiffGeo.MDiff] "Couldn't find a normed space structure on {H}` either: \ + assuming it is a non-trivially normed field" + -- Return the trivial model with corners: this will work if `H` is a normed field. + let eT : Term ← Term.exprToSyntax H + let iTerm : Term ← ``(𝓘($eT)) + Term.elabTerm iTerm none return m /-- Attempt to find a model with corners on a space of continuous linear maps -/ -- Note that (continuous) linear equivalences are not an abelian group, so are not a model with @@ -497,18 +530,13 @@ where -- the standard model with corners. -- Therefore, we only check definitional equality at reducible transparency. let (k, _E, _F) ← isCLMReduciblyDefeqCoefficients e - let eK : Term ← Term.exprToSyntax k - let eT : Term ← Term.exprToSyntax e - let iTerm : Term ← ``(𝓘($eK, $eT)) - Term.elabTerm iTerm none + mkAppOptM ``modelWithCornersSelf #[k, none, e, none, none] /-- Attempt to find a model with corners on a Euclidean space, half-space or quadrant -/ fromEuclideanSpace : TermElabM Expr := do -- We don't use `match_expr` to avoid importing `EuclideanHalfSpace`. match (← instantiateMVars e).cleanupAnnotations with | mkApp2 (.const `EuclideanSpace _) k _n => - let eK : Term ← Term.exprToSyntax k - let eT : Term ← Term.exprToSyntax e - Term.elabTerm (← ``(𝓘($eK, $eT))) none + mkAppOptM ``modelWithCornersSelf #[k, none, e, none, none] | mkApp2 (.const `EuclideanHalfSpace _) n _ => mkAppOptM `modelWithCornersEuclideanHalfSpace #[n, none] | mkApp (.const `EuclideanQuadrant _) n => @@ -569,9 +597,7 @@ where | _ => return none if let some (k, R) := searchNormedAlgebra then trace[Elab.DiffGeo.MDiff] "found a normed algebra: `{α}` is a normed `{k}`-algebra" - let eK : Term ← Term.exprToSyntax k - let eR : Term ← Term.exprToSyntax R - Term.elabTerm (← ``(𝓘($eK, $eR))) none + mkAppOptM ``modelWithCornersSelf #[k, none, R, none, none] else trace[Elab.DiffGeo.MDiff] "`{α}` is not a normed algebra on the nose: try via a space of \ continuous linear maps" @@ -596,9 +622,7 @@ where match normedSpace? with | some (k, _R) => trace[Elab.DiffGeo.MDiff] "found a normed space: `{V}` is a normed space over `{k}`" - let eK : Term ← Term.exprToSyntax k - let eα : Term ← Term.exprToSyntax α - Term.elabTerm (← ``(𝓘($eK, $eα))) none + mkAppOptM ``modelWithCornersSelf #[k, none, α, none, none] | _ => throwError "Found no `NormedSpace` structure on `{V}` among local instances" else -- NB. If further instances of `NormedAlgebra` arise in practice, adding another check @@ -687,12 +711,8 @@ where | _ => throwError "`{e}` is not a sphere in a real normed space" /-- Attempt to find a model with corners from a normed field. We attempt to find a global instance here. -/ - fromNormedField : TermElabM Expr := do - let eT : Term ← Term.exprToSyntax e - let iTerm : Term ← ``(𝓘($eT, $eT)) - Term.elabTerm iTerm none + fromNormedField : TermElabM Expr := mkAppOptM ``modelWithCornersSelf #[e, none, e, none, none] -set_option linter.style.emptyLine false in -- linter false positive /-- Try to find a `ModelWithCorners` instance on a type (represented by an expression `e`), using the local context to infer the appropriate instance. This supports all `ModelWithCorners` instances that are currently defined in mathlib. @@ -700,11 +720,6 @@ Further cases can be added as necessary. Return an expression describing the found model with corners. -`baseInfo` is only used for the first case, a model with corners on the total space of the vector -bundle. In this case, it contains a pair of expressions `(e, i)` describing the type of the base -and the model with corners on the base: these are required to construct the right model with -corners. - Note that the matching on `e` does not see through reducibility (e.g. we distinguish the `abbrev` `TangentBundle` from its definition), so `whnfR` should not be run on `e` prior to calling `findModel` on it. @@ -718,23 +733,23 @@ This implementation is not maximally robust yet. -- This should not be an issue in practice. -- FIXME: can one prove this terminates w.r.t. a suitable measure? This is only recursing into -- subexpressions (at least, after match_expr), right? -partial def findModel (e : Expr) (baseInfo : Option (Expr × Expr) := none) : TermElabM Expr := do +partial def findModel (e : Expr) : TermElabM Expr := do trace[Elab.DiffGeo.MDiff] "Finding a model with corners for: `{e}`" - if let some { model .. } ← go e baseInfo then + if let some { model .. } ← go e then return model else let tracing := (← isTracingEnabledFor `Elab.DiffGeo.MDiff) let hint : MessageData := if e.hasExprMVar then .hint' "the expected type contains metavariables, \ maybe you need to provide an implicit argument" - else if tracing then m!"" else - .hint' "failures to find a model with corners can be debugged with the \ + else if tracing then m!"" + else .hint' "failures to find a model with corners can be debugged with the \ command `set_option trace.Elab.DiffGeo.MDiff true`." throwError "Could not find a model with corners for `{e}`.{hint}" where - go (e : Expr) (baseInfo : Option (Expr × Expr)) : TermElabM (Option FindModelResult) := do + go (e : Expr) : TermElabM (Option FindModelResult) := do -- At first, try finding a model with corners on the space itself. - if let some m ← findModelInner e baseInfo then return some m + if let some m ← findModelInner e then return some m -- Otherwise, we recurse into the expression, -- depending whether we have an open subset of a space, a product, or a direct sum of spaces. match_expr e with @@ -755,30 +770,27 @@ where trace[Elab.DiffGeo.MDiff] "`{e}` is an open set of `{M}`, finding a model on `{M}`" -- `M` is not a open set of another manifold, as `Opens X` is (currently) not a -- topological space (and this would be strange). Therefore, do not recurse into `M`. - go M baseInfo + go M | _ => return none | _ => return none | _ => return none | _ => return none | Prod E F => trace[Elab.DiffGeo.MDiff] "Expression `{e}` is a product, recursing into each factor" - let some { model := srcE, normedSpaceInfo? := normedSpaceE } ← go E baseInfo + let some { model := srcE, normedSpaceInfo? := normedSpaceE } ← go E | throwError "Found no model with corners on first factor `{E}`" - let some { model := srcF, normedSpaceInfo? := normedSpaceF } ← go F baseInfo + let some { model := srcF, normedSpaceInfo? := normedSpaceF } ← go F | throwError "Found no model with corners on second factor `{F}`" -- If both E and F are normed spaces, we have ambiguity: warn and exit. if normedSpaceE.isSome && normedSpaceF.isSome then throwError "`{e}` is a product of normed spaces, so there are two potential models with \ corners\nFor now, please specify the model by hand." -- Otherwise, we are not a normed space, and normally form the product model. - let eTerm : Term ← Term.exprToSyntax srcE - let fTerm : Term ← Term.exprToSyntax srcF - let iTerm : Term ← ``(ModelWithCorners.prod $eTerm $fTerm) - return some { model := ← Term.elabTerm iTerm none } + return some { model := ← mkAppM ``ModelWithCorners.prod #[srcE, srcF] } | Sum E F => trace[Elab.DiffGeo.MDiff] "Expression `{e}` is a direct sum of `{E}` and `{F}`\n\ We assume the models match, and only look into the first summand" - go E baseInfo + go E | _ => return none /-- If the type of `e` is a non-dependent function between spaces `src` and `tgt`, try to find a @@ -804,7 +816,7 @@ def findModels (e : Expr) (es : Option Expr) : TermElabM (Expr × Expr) := do if !(← isDefEq estype <| ← mkAppM ``Set #[src]) then throwError "The domain `{src}` of `{e}` is not definitionally equal to the carrier type of \ the set `{es}` : `{estype}`" - let tgtI ← findModel tgt (src, srcI) + let tgtI ← findModel tgt return (srcI, tgtI) | _ => throwError "Expected{indentD e}\nof type{indentD etype}\nto be a function" @@ -967,6 +979,29 @@ scoped elab:max "tangentMap%" ppSpace f:term:arg : term => do let (srcI, tgtI) ← findModels ef none mkAppM ``tangentMap #[srcI, tgtI, ef] +/-- `UniqueMDiff[s]` elaborates to `UniqueMDiffOn I s`, +trying to determine `I` from the local context. -/ +scoped elab:max "UniqueMDiff[" s:term "]" : term => do + let es ← Term.elabTerm s none + let estype : Expr ← inferType es + match_expr estype with + | Set α => + let I ← findModel α + mkAppM ``UniqueMDiffOn #[I, es] + | _ => throwError "`{es}` has type `{estype}` which is not of the form `Set α` for some `α`." + +/-- `UniqueMDiffAt[s] x` elaborates to `UniqueMDiffWithinAt I s x` +trying to determine `I` from the local context. +The argument `x` can be omitted. -/ +scoped elab:max "UniqueMDiffAt[" s:term "]" : term => do + let es ← Term.elabTerm s none + let estype : Expr ← inferType es + match_expr estype with + | Set α => + let I ← findModel α + mkAppM ``UniqueMDiffWithinAt #[I, es] + | _ => throwError "`{es}` has type `{estype}` which is not of the form `Set α` for some `α`." + end Manifold section trace @@ -1046,7 +1081,25 @@ arguments that can use the `T%` elaborator. -/ let fs ← withAppArg delab `(mfderiv% $fs) >>= annotateGoToSyntaxDef --- TODO: add a delaborator for mfderivWithin (with a test) +/-- Delaborator for `mfderivWithin` using the custom elaborator, and special-casing +arguments that can use the `T%` elaborator. -/ +@[app_delab mfderivWithin] meta def delabMFDerivWithin : Delab := do + whenPPOption getPPNotation do + withOverApp 22 do + let ss ← withAppArg delab + try + let fe := (← getExpr).getAppArgs[20]! + let .lam n _ b _ := fe | failure + guard <| b.isAppOf ``Bundle.TotalSpace.mk' + let σe := b.getAppArgs[4]!.getAppFn + guard <| σe.isFVar + let Tσs ← withNaryArg 20 do + let σs ← withBindingBody n <| withNaryArg 4 <| withNaryFn delab + `(T% $σs) >>= annotateGoToSyntaxDef + `(mfderiv[$ss] ($Tσs)) >>= annotateGoToSyntaxDef + catch _ => + let fs ← withNaryArg 20 delab + `(mfderiv[$ss] $fs) >>= annotateGoToSyntaxDef /-- Delaborator for `MDifferentiable` using the custom elaborator, and special-casing arguments that can use the `T%` elaborator. -/ @@ -1126,8 +1179,69 @@ arguments that can use the `T%` elaborator. -/ let fs ← withNaryArg 20 <| delab `(MDiffAt[$ss] $fs) >>= annotateGoToSyntaxDef +/-- Delaborator for `HasMFDerivWithinAt` using the custom elaborator, and special-casing +arguments that can use the `T%` elaborator. -/ +@[app_delab HasMFDerivWithinAt] meta def delabHasMFDerivWithinAt : Delab := do + whenPPOption getPPNotation do + withOverApp 24 do + let ss ← withNaryArg 21 delab + let xs ← withNaryArg 22 delab + let f' ← withNaryArg 23 delab + try + let f := (← getExpr).getAppArgs[20]! + let .lam n _ b _ := f | failure + guard <| b.isAppOf ``Bundle.TotalSpace.mk' + let s := b.getAppArgs[4]!.getAppFn + guard <| s.isFVar + let σe := b.getAppArgs[4]!.getAppFn + guard <| σe.isFVar + let Tσs ← withNaryArg 20 do + let σs ← withBindingBody n <| withNaryArg 4 <| withNaryFn delab + `((T% $σs)) >>= annotateGoToSyntaxDef + `(HasMFDerivAt[$ss] $Tσs $xs $f') >>= annotateGoToSyntaxDef + catch _ => + let fs ← withNaryArg 20 delab + `(HasMFDerivAt[$ss] $fs $xs $f') >>= annotateGoToSyntaxDef + +/-- Delaborator for `HasMFDerivWithinAt` using the custom elaborator, and special-casing +arguments that can use the `T%` elaborator. -/ +@[app_delab HasMFDerivAt] meta def delabHasMFDerivAt : Delab := do + whenPPOption getPPNotation do + withOverApp 23 do + let xs ← withNaryArg 21 delab + let f' ← withNaryArg 22 delab + try + let f := (← getExpr).getAppArgs[20]! + let .lam n _ b _ := f | failure + guard <| b.isAppOf ``Bundle.TotalSpace.mk' + let s := b.getAppArgs[4]!.getAppFn + guard <| s.isFVar + let σe := b.getAppArgs[4]!.getAppFn + guard <| σe.isFVar + let Tσs ← withNaryArg 20 do + let σs ← withBindingBody n <| withNaryArg 4 <| withNaryFn delab + `((T% $σs)) >>= annotateGoToSyntaxDef + `(HasMFDerivAt% $Tσs $xs $f') >>= annotateGoToSyntaxDef + catch _ => + let fs ← withNaryArg 20 delab + `(HasMFDerivAt% $fs $xs $f') >>= annotateGoToSyntaxDef + +/-- Delaborator for `UniqueMDiffOn` using the custom elaborator. -/ +@[app_delab UniqueMDiffOn] meta def delabUniqueMDiffOn : Delab := do + whenPPOption getPPNotation do + withOverApp 12 do + let ss ← withAppArg delab + `(UniqueMDiff[$ss]) >>= annotateGoToSyntaxDef + +/-- Delaborator for `UniqueMDiffWithinAt` using the custom elaborator. -/ +@[app_delab UniqueMDiffWithinAt] meta def delabUniqueMDiffWithinAt : Delab := do + whenPPOption getPPNotation do + withOverApp 12 do + let ss ← withAppArg delab + `(UniqueMDiffAt[$ss]) >>= annotateGoToSyntaxDef + -- TODO: add more delaborators (and tests) for --- ContMDiff, ContMDiffOn, ContMDiffAt, ContMDiffWithinAt, HasMFDerivAt, HasMFDerivWithinAt +-- ContMDiff, ContMDiffOn, ContMDiffAt, ContMDiffWithinAt -- TODO: when adding more elaborators, also add the corresponding delaborators diff --git a/Mathlib/Geometry/Manifold/Riemannian/Basic.lean b/Mathlib/Geometry/Manifold/Riemannian/Basic.lean index 7ddda591cfd451..aca6592592b743 100644 --- a/Mathlib/Geometry/Manifold/Riemannian/Basic.lean +++ b/Mathlib/Geometry/Manifold/Riemannian/Basic.lean @@ -363,7 +363,7 @@ lemma eventually_riemannianEDist_le_edist_extChartAt (x : M) : have : mfderiv[Icc 0 1] γ t 1 = (mfderiv[range I] (extChartAt I x).symm (η t)) (mfderiv[Icc 0 1] η t 1) := congr($this 1) rw [this] - apply (ContinuousLinearMap.le_opNorm_enorm _ _).trans + apply (ContinuousLinearMap.le_opENorm _ _).trans gcongr · exact (hη.2 ht).le · simp only [mfderivWithin_eq_fderivWithin] @@ -474,7 +474,7 @@ lemma setOf_riemannianEDist_lt_subset_nhds [RegularSpace M] {x : M} {s : Set M} (mfderiv% (extChartAt I x) (γ t')) (mfderiv[Icc 0 t₁] γ t' 1) := congr($this 1) rw [this] - apply (ContinuousLinearMap.le_opNorm_enorm _ _).trans + apply (ContinuousLinearMap.le_opENorm _ _).trans gcongr refine (hu ?_).le apply t₁_mem ht' diff --git a/Mathlib/Geometry/Manifold/SmoothEmbedding.lean b/Mathlib/Geometry/Manifold/SmoothEmbedding.lean index 14656f059daa50..622c8297de17e2 100644 --- a/Mathlib/Geometry/Manifold/SmoothEmbedding.lean +++ b/Mathlib/Geometry/Manifold/SmoothEmbedding.lean @@ -22,6 +22,7 @@ This will be useful to define embedded submanifolds. * `IsSmoothEmbedding.id`: the identity map is a smooth embedding * `IsSmoothEmbedding.of_opens`: the inclusion of an open subset `s → M` of a smooth manifold is a smooth embedding +* `ModelWithCorners.isSmoothEmbedding`: every model with corners is itself a smooth embedding * `IsSmoothEmbedding.sumInl` and `IsSmoothEmbedding.sumInr`: given `C^n` manifolds `M` and `N`, `Sum.inl : M → M ⊕ N` and `Sum.inr : N → M ⊕ N` are `C^n` embeddings * `IsSmoothEmbedding.contMDiff`: if `f` is a `C^n` embedding, it is automatically `C^n` @@ -94,6 +95,11 @@ lemma of_opens [IsManifold I n M] (s : TopologicalSpace.Opens M) : rw [isSmoothEmbedding_iff] exact ⟨IsImmersion.of_opens s, IsEmbedding.subtypeVal⟩ +/-- Every `ModelWithCorners 𝕜 E H` is a smooth embedding when viewed as a map `H → E`. -/ +protected lemma _root_.ModelWithCorners.isSmoothEmbedding {n : ℕ} : + IsSmoothEmbedding I (modelWithCornersSelf 𝕜 E₁) n I := + ⟨I.isImmersion, I.isClosedEmbedding.isEmbedding⟩ + /-- Given `C^n` manifolds `M` and `N`, `Sum.inl : M → M ⊕ N` is a `C^n` embedding. -/ lemma sumInl {M' : Type*} [TopologicalSpace M'] [ChartedSpace H M'] [IsManifold I n M] [IsManifold I n M'] : IsSmoothEmbedding I I n (@Sum.inl M M') := diff --git a/Mathlib/Geometry/Manifold/Submersion.lean b/Mathlib/Geometry/Manifold/Submersion.lean index af48f831ada3a6..95d305ffd1405f 100644 --- a/Mathlib/Geometry/Manifold/Submersion.lean +++ b/Mathlib/Geometry/Manifold/Submersion.lean @@ -5,10 +5,12 @@ Authors: Michael Rothgang, Samantha Naranjo Guevara -/ module -public import Mathlib.Geometry.Manifold.IsManifold.ExtChartAt public import Mathlib.Geometry.Manifold.LocalSourceTargetProperty public import Mathlib.Analysis.Normed.Module.Shrink public import Mathlib.Topology.Algebra.Module.TransferInstance +public import Mathlib.Geometry.Manifold.ContMDiff.Atlas +public import Mathlib.Geometry.Manifold.ContMDiff.NormedSpace +public import Mathlib.Geometry.Manifold.Notation /-! # Smooth submersions @@ -46,6 +48,9 @@ if there exist charts near `x` and `f x` in which `f` looks like the standard pr the set of points where `IsSubmersionAt(OfComplement)` holds is open. * `IsSubmersionAt.prodMap` and `IsSubmersion.prodMap`: the product of two submersions (at a point) is a submersion (at the product point). +* `IsSubmersionAt.contMDiffAt`: if `f` is a submersion at `x`, it is `C^n` at `x`. +* `IsSubmersion.contMDiff`: if `f` is a submersion, it is automatically `C^n` + in the sense of `ContMDiff`. ## Implementation notes @@ -58,8 +63,6 @@ The implementation strategy is identical to the one for immersions. See the impl ## TODO * The converse to `IsSubmersionAtOfComplement.congr_F` also holds: any two complements are isomorphic, as they are isomorphic to the kernel of the differential `mfderiv I J f x`. -* `IsSubmersionAt.contMDiffAt`: if f is a submersion at `x`, it is `C^n` at `x`. -* `IsSubmersion.contMDiff`: if f is a submersion, it is `C^n`. * If `f` is a submersion at `x`, its differential `mfderiv I J f x` admits a continuous right inverse, in particular is surjective. * If `f : M → N` is a map between Banach manifolds, `mfderiv I J f x` having a continuous right @@ -87,9 +90,8 @@ This will be the topic of Samantha Naranjo's master's thesis, and it's nice to c public noncomputable section -open scoped Topology ContDiff - -open Function Set +open scoped Topology ContDiff Manifold +open OpenPartialHomeomorph Function Set namespace Manifold @@ -142,7 +144,7 @@ NB. We don't know the particular atlasses used for `M` and `N`, so asking for ` in the `atlas` would be too optimistic: lying in the `maximalAtlas` is sufficient. This definition has a fixed parameter `F`, which is a choice of complement of `E''` in `E`: -being an immersion at `x` includes a choice of linear isomorphism between `E'' × F` and `E`. +being an submersion at `x` includes a choice of linear isomorphism between `E'' × F` and `E`. While the particular choice of complement is often not important, choosing a complement is useful in some settings, such as proving that embedded submanifolds are locally given either by an immersion or a submersion. @@ -236,6 +238,10 @@ lemma source_subset_preimage_source (h : IsSubmersionAtOfComplement F I J n f x) h.domChart.source ⊆ f ⁻¹' h.codChart.source := LiftSourceTargetPropertyAt.source_subset_preimage_source h +lemma mapsto_domChart_source_codChart_source (h : IsSubmersionAtOfComplement F I J n f x) : + MapsTo f h.domChart.source h.codChart.source := + h.source_subset_preimage_source + /-- A linear equivalence `E ≃L[𝕜] E'' × F` which belongs to the data of a submersion `f` at `x`: the particular equivalence is arbitrary, but this choice matches the witnesses given by `h.domChart` and `h.codChart`. -/ @@ -353,6 +359,31 @@ lemma isSubmersionAt (h : IsSubmersionAtOfComplement F I J n f x) : use h.smallComplement, by infer_instance, by infer_instance exact (IsSubmersionAtOfComplement.congr_F h.smallEquiv).mp h +/-- If `f` is a `C^n` submersion at `x`, then `f` is `C^n` on its domain chart's source, +in particular on an open neighbourhood of `x`. + +Prefer using `IsSubmersionAtOfComplement.contMDiffAt` instead. -/ +theorem contMDiffOn (h : IsSubmersionAtOfComplement F I J n f x) : + ContMDiffOn I J n f h.domChart.source := by + rw [← contMDiffOn_writtenInExtend_iff h.domChart_mem_maximalAtlas + h.codChart_mem_maximalAtlas le_rfl h.mapsto_domChart_source_codChart_source, + ← h.domChart.extend_target_eq_image_source] + have : CMDiff n (Prod.fst ∘ h.equiv) := by + -- Note that we cannot use `h₁.comp contMDiff_fst` since `h₁` and `contMDiff_fst` require + -- different models with corners on `E'' × F`. The former uses `𝓘(𝕜, E'' × F)` while the latter + -- uses `(𝓘(𝕜, E'')).prod (𝓘(𝕜, F)`. + have h₁ : ContMDiff 𝓘(𝕜, E) 𝓘(𝕜, E'' × F) n h.equiv := by + rw [contMDiff_iff_contDiff] + exact h.equiv.contDiff + apply ContMDiff.comp ?_ h₁ + rw [contMDiff_iff_contDiff] + exact contDiff_fst + exact this.contMDiffOn.congr h.writtenInCharts + +/-- A `C^n` submersion at `x` is `C^n` at `x`. -/ +theorem contMDiffAt (h : IsSubmersionAtOfComplement F I J n f x) : CMDiffAt n f x := + h.contMDiffOn.contMDiffAt (h.domChart.open_source.mem_nhds (mem_domChart_source h)) + end IsSubmersionAtOfComplement namespace IsSubmersionAt @@ -492,6 +523,17 @@ theorem prodMap {f : M → N} {g : M' → N'} {x' : M'} hf.isSubmersionAtOfComplement_complement.prodMap hg.isSubmersionAtOfComplement_complement |>.isSubmersionAt +/-- If `f` is a submersion at `x`, then `f` is `C^n` on its domain chart's source, +in particular on an open neighbourhood of `x`.` + +Prefer using `IsSubmersionAt.contMDiffAt` instead -/ +theorem contMDiffOn (h : IsSubmersionAt I J n f x) : CMDiff[h.domChart.source] n f := + h.isSubmersionAtOfComplement_complement.contMDiffOn + +/-- A `C^n` submersion at `x` is `C^n` at `x`. -/ +theorem contMDiffAt (h : IsSubmersionAt I J n f x) : CMDiffAt n f x := + h.isSubmersionAtOfComplement_complement.contMDiffAt + end IsSubmersionAt variable (F I J n) in @@ -576,6 +618,10 @@ protected lemma id [IsManifold I n M] : IsSubmersionOfComplement PUnit I I n (@i rw [(chartAt H x).right_inv (by simp_all), I.right_inv (by simp_all)] simpa +/-- A `C^n` submersion is `C^n` -/ +theorem contMDiff (h : IsSubmersionOfComplement F I J n f) : CMDiff n f := + fun x ↦ (h x).contMDiffAt + end IsSubmersionOfComplement namespace IsSubmersion @@ -616,6 +662,10 @@ protected lemma id [IsManifold I n M] : IsSubmersion I I n (@id M) := by use PUnit, by infer_instance, by infer_instance exact IsSubmersionOfComplement.id +/-- A `C^n` submersion is `C^n` -/ +theorem contMDiff (h : IsSubmersion I J n f) : CMDiff n f := + h.isSubmersionOfComplement_complement.contMDiff + end IsSubmersion end Manifold diff --git a/Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean b/Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean index 740d390849c3eb..021464573225ca 100644 --- a/Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean +++ b/Mathlib/Geometry/Manifold/VectorBundle/CovariantDerivative/Metric.lean @@ -33,7 +33,7 @@ metric `g` if and only if the differentiated metric tensor `∇ g` (defined by ## TODO * When Mathlib has a notion of parallel transport, prove the equivalence of - `CovariantDerivative.IsMetricCompatible` with the characterisation that parallel transport be an + `CovariantDerivative.IsMetricCompatible` with the characterisation that parallel transport be an isometry. * Given connections on bundles `V` and `W`, there is an induced connnection on the bundle diff --git a/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean b/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean index 20f7437e4eace3..661dd8ab0a85c0 100644 --- a/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean +++ b/Mathlib/Geometry/Manifold/VectorBundle/Hom.lean @@ -54,9 +54,8 @@ theorem contMDiffOn_continuousLinearMapCoordChange [ContMDiffVectorBundle n F₁ E₁ IB] [ContMDiffVectorBundle n F₂ E₂ IB] [MemTrivializationAtlas e₁] [MemTrivializationAtlas e₁'] [MemTrivializationAtlas e₂] [MemTrivializationAtlas e₂'] : - ContMDiffOn IB 𝓘(𝕜, (F₁ →L[𝕜] F₂) →L[𝕜] F₁ →L[𝕜] F₂) n - (continuousLinearMapCoordChange (RingHom.id 𝕜) e₁ e₁' e₂ e₂') - (e₁.baseSet ∩ e₂.baseSet ∩ (e₁'.baseSet ∩ e₂'.baseSet)) := by + CMDiff[e₁.baseSet ∩ e₂.baseSet ∩ (e₁'.baseSet ∩ e₂'.baseSet)] n + (continuousLinearMapCoordChange (RingHom.id 𝕜) e₁ e₁' e₂ e₂') := by have h₁ := contMDiffOn_coordChangeL (IB := IB) e₁' e₁ (n := n) have h₂ := contMDiffOn_coordChangeL (IB := IB) e₂ e₂' (n := n) refine (h₁.mono ?_).cle_arrowCongr (h₂.mono ?_) <;> mfld_set_tac @@ -72,14 +71,14 @@ theorem hom_chart (y₀ y : LE₁E₂) : hom_trivializationAt_apply] theorem contMDiffWithinAt_hom_bundle (f : M → LE₁E₂) {s : Set M} {x₀ : M} : - ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n f s x₀ ↔ + CMDiffAt[s] n f x₀ ↔ CMDiffAt[s] n (fun x ↦ (f x).1) x₀ ∧ CMDiffAt[s] n (fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ := contMDiffWithinAt_totalSpace theorem contMDiffAt_hom_bundle (f : M → LE₁E₂) {x₀ : M} : - ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n f x₀ ↔ + CMDiffAt n f x₀ ↔ CMDiffAt n (fun x ↦ (f x).1) x₀ ∧ CMDiffAt n (fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ := contMDiffAt_totalSpace @@ -101,14 +100,14 @@ theorem mdifferentiableOn_continuousLinearMapCoordChange variable [∀ x, IsTopologicalAddGroup (E₂ x)] [∀ x, ContinuousSMul 𝕜 (E₂ x)] theorem mdifferentiableWithinAt_hom_bundle (f : M → LE₁E₂) {s : Set M} {x₀ : M} : - MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) f s x₀ ↔ + MDiffAt[s] f x₀ ↔ MDiffAt[s] (fun x ↦ (f x).1) x₀ ∧ MDiffAt[s] (fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ := mdifferentiableWithinAt_totalSpace IB .. theorem mdifferentiableAt_hom_bundle (f : M → LE₁E₂) {x₀ : M} : - MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) f x₀ ↔ + MDiffAt f x₀ ↔ MDiffAt (fun x ↦ (f x).1) x₀ ∧ MDiffAt (fun x ↦ inCoordinates F₁ E₁ F₂ E₂ (f x₀).1 (f x).1 (f x₀).1 (f x).1 (f x).2) x₀ := mdifferentiableAt_totalSpace .. @@ -178,9 +177,8 @@ For a version with `B₁ = B₂` and `b₁ = b₂`, in which smoothness can be e lemma ContMDiffWithinAt.clm_apply_of_inCoordinates (hϕ : CMDiffAt[s] n (fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀) - (hv : ContMDiffWithinAt IM (IB₁.prod 𝓘(𝕜, F₁)) n (fun m ↦ (v m : TotalSpace F₁ E₁)) s m₀) - (hb₂ : CMDiffAt[s] n b₂ m₀) : - ContMDiffWithinAt IM (IB₂.prod 𝓘(𝕜, F₂)) n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) s m₀ := by + (hv : CMDiffAt[s] n (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) (hb₂ : CMDiffAt[s] n b₂ m₀) : + CMDiffAt[s] n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by rw [← contMDiffWithinAt_insert_self] at hϕ hv hb₂ ⊢ rw [contMDiffWithinAt_totalSpace] at hv ⊢ refine ⟨hb₂, ?_⟩ @@ -215,9 +213,8 @@ For a version with `B₁ = B₂` and `b₁ = b₂`, in which smoothness can be e -/ lemma ContMDiffAt.clm_apply_of_inCoordinates (hϕ : CMDiffAt n (fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀) - (hv : ContMDiffAt IM (IB₁.prod 𝓘(𝕜, F₁)) n (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) - (hb₂ : CMDiffAt n b₂ m₀) : - ContMDiffAt IM (IB₂.prod 𝓘(𝕜, F₂)) n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by + (hv : CMDiffAt n (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) (hb₂ : CMDiffAt n b₂ m₀) : + CMDiffAt n (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by rw [← contMDiffWithinAt_univ] at hϕ hv hb₂ ⊢ exact ContMDiffWithinAt.clm_apply_of_inCoordinates hϕ hv hb₂ @@ -261,12 +258,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is `C^n`. We give here a version of this statement within a set at a point. -/ lemma ContMDiffWithinAt.clm_bundle_apply - (hϕ : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) - s x) - (hv : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x) : - ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₂)) n - (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s x := by + (hϕ : CMDiffAt[s] n + (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x) + (hv : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) : + CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := by simp only [contMDiffWithinAt_hom_bundle] at hϕ exact hϕ.2.clm_apply_of_inCoordinates hv hϕ.1 @@ -276,10 +271,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is `C^n`. We give here a version of this statement at a point. -/ lemma ContMDiffAt.clm_bundle_apply - (hϕ : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n + (hϕ : CMDiffAt n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x) - (hv : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) : - ContMDiffAt IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := + (hv : CMDiffAt n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) : + CMDiffAt n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := ContMDiffWithinAt.clm_bundle_apply hϕ hv /-- Consider a `C^n` map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and @@ -288,20 +283,20 @@ One can apply `ϕ m` to `v m`, and the resulting map is `C^n`. We give here a version of this statement on a set. -/ lemma ContMDiffOn.clm_bundle_apply - (hϕ : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) s) - (hv : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s) : - ContMDiffOn IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s := + (hϕ : CMDiff[s] n + (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m))) + (hv : CMDiff[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) : + CMDiff[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) := fun x hx ↦ (hϕ x hx).clm_bundle_apply (hv x hx) /-- Consider a `C^n` map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and linear maps `ϕ m : E₁ (b m) → E₂ (b m)` depending smoothly on `m`. One can apply `ϕ m` to `v m`, and the resulting map is `C^n`. -/ lemma ContMDiff.clm_bundle_apply - (hϕ : ContMDiff IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) n + (hϕ : CMDiff n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m))) - (hv : ContMDiff IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) : - ContMDiff IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) := + (hv : CMDiff n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) : + CMDiff n (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) := fun x ↦ (hϕ x).clm_bundle_apply (hv x) end OneVariable @@ -317,13 +312,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is differentiable. We give here a version of this statement within a set at a point. -/ lemma MDifferentiableWithinAt.clm_bundle_apply - (hϕ : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) - s x) - (hv : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁)) - (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x) : - MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₂)) - (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s x := by + (hϕ : MDiffAt[s] + (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x) + (hv : MDiffAt[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) : + MDiffAt[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := by simp only [mdifferentiableWithinAt_hom_bundle] at hϕ exact hϕ.2.clm_apply_of_inCoordinates hv hϕ.1 @@ -333,10 +325,10 @@ One can apply `ϕ m` to `v m`, and the resulting map is differentiable. We give here a version of this statement at a point. -/ lemma MDifferentiableAt.clm_bundle_apply - (hϕ : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) + (hϕ : MDiffAt (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) x) - (hv : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) : - MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := + (hv : MDiffAt (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) : + MDiffAt (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) x := MDifferentiableWithinAt.clm_bundle_apply hϕ hv /-- Consider a differentiable map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and @@ -345,20 +337,20 @@ One can apply `ϕ m` to `v m`, and the resulting map is differentiable. We give here a version of this statement on a set. -/ lemma MDifferentiableOn.clm_bundle_apply - (hϕ : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m)) s) - (hv : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s) : - MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) s := + (hϕ : MDiff[s] + (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m))) + (hv : MDiff[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) : + MDiff[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) := fun x hx ↦ (hϕ x hx).clm_bundle_apply (hv x hx) /-- Consider a differentiable map `v : M → E₁` to a vector bundle, over a base map `b : M → B`, and linear maps `ϕ m : E₁ (b m) → E₂ (b m)` depending smoothly on `m`. One can apply `ϕ m` to `v m`, and the resulting map is differentiable. -/ lemma MDifferentiable.clm_bundle_apply - (hϕ : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂)) + (hϕ : MDiff (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x)) (b m) (ϕ m))) - (hv : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) : - MDifferentiable IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) := + (hv : MDiff (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) : + MDiff (fun m ↦ TotalSpace.mk' F₂ (b m) (ϕ m (v m))) := fun x ↦ (hϕ x).clm_bundle_apply (hv x) end OneVariable' @@ -368,62 +360,54 @@ section TwoVariables variable [∀ x, IsTopologicalAddGroup (E₃ x)] [∀ x, ContinuousSMul 𝕜 (E₃ x)] {ψ : ∀ x, (E₁ (b x) →L[𝕜] E₂ (b x) →L[𝕜] E₃ (b x))} {w : ∀ x, E₂ (b x)} -/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`. We give here a version of this statement within a set at a point. -/ lemma ContMDiffWithinAt.clm_bundle_apply₂ - (hψ : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) - (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s x) - (hv : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x) - (hw : ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s x) : - ContMDiffWithinAt IM (IB.prod 𝓘(𝕜, F₃)) n - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s x := + (hψ : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x) + (hv : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) + (hw : CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) : + CMDiffAt[s] n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x := hψ.clm_bundle_apply hv |>.clm_bundle_apply hw -/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`. We give here a version of this statement at a point. -/ lemma ContMDiffAt.clm_bundle_apply₂ - (hψ : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (hψ : CMDiffAt n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x) - (hv : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) - (hw : ContMDiffAt IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) : - ContMDiffAt IM (IB.prod 𝓘(𝕜, F₃)) n - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x := + (hv : CMDiffAt n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) + (hw : CMDiffAt n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) : + CMDiffAt n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x := ContMDiffWithinAt.clm_bundle_apply₂ hψ hv hw -/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`. We give here a version of this statement on a set. -/ lemma ContMDiffOn.clm_bundle_apply₂ - (hψ : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) - (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s) - (hv : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s) - (hw : ContMDiffOn IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s) : - ContMDiffOn IM (IB.prod 𝓘(𝕜, F₃)) n - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s := + (hψ : CMDiff[s] n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m))) + (hv : CMDiff[s] n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) + (hw : CMDiff[s] n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) : + CMDiff[s] n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) := fun x hx ↦ (hψ x hx).clm_bundle_apply₂ (hv x hx) (hw x hx) -/-- Consider `C^n` maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider `C^n` maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is `C^n`. -/ lemma ContMDiff.clm_bundle_apply₂ - (hψ : ContMDiff IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) n - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (hψ : CMDiff n (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m))) - (hv : ContMDiff IM (IB.prod 𝓘(𝕜, F₁)) n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) - (hw : ContMDiff IM (IB.prod 𝓘(𝕜, F₂)) n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) : - ContMDiff IM (IB.prod 𝓘(𝕜, F₃)) n - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) := + (hv : CMDiff n (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) + (hw : CMDiff n (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) : + CMDiff n (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) := fun x ↦ (hψ x).clm_bundle_apply₂ (hv x) (hw x) end TwoVariables @@ -433,64 +417,54 @@ section TwoVariables' variable [∀ x, IsTopologicalAddGroup (E₃ x)] [∀ x, ContinuousSMul 𝕜 (E₃ x)] {ψ : ∀ x, (E₁ (b x) →L[𝕜] E₂ (b x) →L[𝕜] E₃ (b x))} {w : ∀ x, E₂ (b x)} -/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable. We give here a version of this statement within a set at a point. -/ lemma MDifferentiableWithinAt.clm_bundle_apply₂ - (hψ : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) - (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s x) - (hv : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₁)) - (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s x) - (hw : MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₂)) - (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s x) : - MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F₃)) - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s x := + (hψ : MDiffAt[s] (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x) + (hv : MDiffAt[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) + (hw : MDiffAt[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) : + MDiffAt[s] (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x := hψ.clm_bundle_apply hv |>.clm_bundle_apply hw -/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable. We give here a version of this statement at a point. -/ lemma MDifferentiableAt.clm_bundle_apply₂ - (hψ : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (hψ : MDiffAt (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) x) - (hv : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) - (hw : MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) : - MDifferentiableAt IM (IB.prod 𝓘(𝕜, F₃)) - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x := + (hv : MDiffAt (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) x) + (hw : MDiffAt (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) x) : + MDiffAt (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) x := MDifferentiableWithinAt.clm_bundle_apply₂ hψ hv hw -/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable. We give here a version of this statement on a set. -/ lemma MDifferentiableOn.clm_bundle_apply₂ - (hψ : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) - (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m)) s) - (hv : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m)) s) - (hw : MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (w m)) s) : - MDifferentiableOn IM (IB.prod 𝓘(𝕜, F₃)) - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) s := + (hψ : MDiff[s] (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m))) + (hv : MDiff[s] (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) + (hw : MDiff[s] (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) : + MDiff[s] (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) := fun x hx ↦ (hψ x hx).clm_bundle_apply₂ (hv x hx) (hw x hx) -/-- Consider differentiable maps `v : M → E₁` and `v : M → E₂` to vector bundles, over a base map +/-- Consider differentiable maps `v : M → E₁` and `w : M → E₂` to vector bundles, over a base map `b : M → B`, and bilinear maps `ψ m : E₁ (b m) → E₂ (b m) → E₃ (b m)` depending smoothly on `m`. One can apply `ψ m` to `v m` and `w m`, and the resulting map is differentiable. -/ lemma MDifferentiable.clm_bundle_apply₂ - (hψ : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁ →L[𝕜] F₂ →L[𝕜] F₃)) - (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) + (hψ : MDiff (fun m ↦ TotalSpace.mk' (F₁ →L[𝕜] F₂ →L[𝕜] F₃) (E := fun (x : B) ↦ (E₁ x →L[𝕜] E₂ x →L[𝕜] E₃ x)) (b m) (ψ m))) - (hv : MDifferentiable IM (IB.prod 𝓘(𝕜, F₁)) (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) - (hw : MDifferentiable IM (IB.prod 𝓘(𝕜, F₂)) (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) : - MDifferentiable IM (IB.prod 𝓘(𝕜, F₃)) - (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) := + (hv : MDiff (fun m ↦ TotalSpace.mk' F₁ (b m) (v m))) + (hw : MDiff (fun m ↦ TotalSpace.mk' F₂ (b m) (w m))) : + MDiff (fun m ↦ TotalSpace.mk' F₃ (b m) (ψ m (v m) (w m))) := fun x ↦ (hψ x).clm_bundle_apply₂ (hv x) (hw x) end TwoVariables' diff --git a/Mathlib/Geometry/Manifold/VectorBundle/LocalFrame.lean b/Mathlib/Geometry/Manifold/VectorBundle/LocalFrame.lean index 48b563d3570b33..a051a620ddc6e5 100644 --- a/Mathlib/Geometry/Manifold/VectorBundle/LocalFrame.lean +++ b/Mathlib/Geometry/Manifold/VectorBundle/LocalFrame.lean @@ -297,7 +297,7 @@ lemma mdifferentiableOn_of_coeff [FiniteDimensional 𝕜 F] have this (i) : MDiff[u] (T% ((LinearMap.piApply (hs.coeff i)) t • s i)) := (h i).smul_section ((hs.contMDiffOn i).mdifferentiableOn one_ne_zero) have almost : MDiff[u] (T% (fun x ↦ ∑ i, hs.coeff i x (t x) • s i x)) := - .sum_section (fun i _ hx ↦ this i _ hx) + .sum_section (fun i _ _ hx ↦ this i _ hx) apply almost.congr intro y hy simpa using congrArg (TotalSpace.mk' F y) (hs.coeff_sum_eq t hy) @@ -309,7 +309,7 @@ lemma mdifferentiableAt_of_coeff [FiniteDimensional 𝕜 F] MDiffAt (T% t) x := by have := fintypeOfFiniteDimensional hs (mem_of_mem_nhds hu) have almost : MDiffAt (T% (fun x ↦ ∑ i, hs.coeff i x (t x) • s i x)) x := - .sum_section (fun i ↦ (h i).smul_section <| + .sum_section (fun i _ ↦ (h i).smul_section <| ((hs.contMDiffOn i).mdifferentiableOn one_ne_zero).mdifferentiableAt hu) exact almost.congr_of_eventuallyEq <| (hs.eventually_eq_sum_coeff_smul t hu).mono (by simp) diff --git a/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean b/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean index b47e572fc3d9b8..417ace8c8b5fe6 100644 --- a/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean +++ b/Mathlib/Geometry/Manifold/VectorBundle/MDifferentiable.lean @@ -39,7 +39,7 @@ variable [TopologicalSpace B] [ChartedSpace HB B] [FiberBundle F E] /-- Characterization of differentiable functions into a vector bundle. Version at a point within a set -/ theorem mdifferentiableWithinAt_totalSpace (f : M → TotalSpace F E) {s : Set M} {x₀ : M} : - MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F)) f s x₀ ↔ + MDiffAt[s] f x₀ ↔ MDiffAt[s] (fun x => (f x).proj) x₀ ∧ MDiffAt[s] (fun x ↦ (trivializationAt F E (f x₀).proj (f x)).2) x₀ := by simp +singlePass only [mdifferentiableWithinAt_iff_target] @@ -61,7 +61,7 @@ theorem mdifferentiableWithinAt_totalSpace (f : M → TotalSpace F E) {s : Set M /-- Characterization of differentiable functions into a vector bundle. Version at a point -/ theorem mdifferentiableAt_totalSpace (f : M → TotalSpace F E) {x₀ : M} : - MDifferentiableAt IM (IB.prod 𝓘(𝕜, F)) f x₀ ↔ + MDiffAt f x₀ ↔ MDiffAt (fun x => (f x).proj) x₀ ∧ MDiffAt (fun x ↦ (trivializationAt F E (f x₀).proj (f x)).2) x₀ := by simpa [← mdifferentiableWithinAt_univ] using mdifferentiableWithinAt_totalSpace _ f @@ -69,8 +69,7 @@ theorem mdifferentiableAt_totalSpace (f : M → TotalSpace F E) {x₀ : M} : /-- Characterization of differentiable sections of a vector bundle at a point within a set in terms of the preferred trivialization at that point. -/ theorem mdifferentiableWithinAt_section (s : Π b, E b) {u : Set B} {b₀ : B} : - MDifferentiableWithinAt IB (IB.prod 𝓘(𝕜, F)) (T% s) u b₀ ↔ - MDiffAt[u] (fun b ↦ (trivializationAt F E b₀ (s b)).2) b₀ := by + MDiffAt[u] (T% s) b₀ ↔ MDiffAt[u] (fun b ↦ (trivializationAt F E b₀ (s b)).2) b₀ := by rw [mdifferentiableWithinAt_totalSpace] change MDifferentiableWithinAt _ _ id _ _ ∧ _ ↔ _ simp [mdifferentiableWithinAt_id] @@ -85,21 +84,19 @@ namespace Bundle variable (E) {IB} -theorem mdifferentiable_proj : MDifferentiable (IB.prod 𝓘(𝕜, F)) IB (π F E) := fun x ↦ by - have : MDifferentiableAt (IB.prod 𝓘(𝕜, F)) (IB.prod 𝓘(𝕜, F)) id x := mdifferentiableAt_id +theorem mdifferentiable_proj : MDiff (π F E) := fun x ↦ by + have : MDiffAt (@id <| TotalSpace F E) x := mdifferentiableAt_id rw [mdifferentiableAt_totalSpace] at this exact this.1 -theorem mdifferentiableOn_proj {s : Set (TotalSpace F E)} : - MDifferentiableOn (IB.prod 𝓘(𝕜, F)) IB (π F E) s := +theorem mdifferentiableOn_proj {s : Set (TotalSpace F E)} : MDiff[s] (π F E) := (mdifferentiable_proj E).mdifferentiableOn -theorem mdifferentiableAt_proj {p : TotalSpace F E} : - MDifferentiableAt (IB.prod 𝓘(𝕜, F)) IB (π F E) p := +theorem mdifferentiableAt_proj {p : TotalSpace F E} : MDiffAt (π F E) p := (mdifferentiable_proj E).mdifferentiableAt theorem mdifferentiableWithinAt_proj {s : Set (TotalSpace F E)} {p : TotalSpace F E} : - MDifferentiableWithinAt (IB.prod 𝓘(𝕜, F)) IB (π F E) s p := + MDiffAt[s] (π F E) p := (mdifferentiableAt_proj E).mdifferentiableWithinAt variable (𝕜) [∀ x, AddCommMonoid (E x)] @@ -243,8 +240,8 @@ theorem mdifferentiableWithinAt_totalSpace_iff (e : Trivialization F (TotalSpace.proj : TotalSpace F E → B)) [MemTrivializationAtlas e] (f : M → TotalSpace F E) {s : Set M} {x₀ : M} (he : f x₀ ∈ e.source) : - MDifferentiableWithinAt IM (IB.prod 𝓘(𝕜, F)) f s x₀ ↔ - MDiffAt[s] (fun x => (f x).proj) x₀ ∧ MDiffAt[s] (fun x ↦ (e (f x)).2) x₀ := by + MDiffAt[s] f x₀ ↔ + MDiffAt[s] (fun x ↦ (f x).proj) x₀ ∧ MDiffAt[s] (fun x ↦ (e (f x)).2) x₀ := by rw [mdifferentiableWithinAt_totalSpace] apply and_congr_right intro hf @@ -257,8 +254,7 @@ theorem mdifferentiableAt_totalSpace_iff (e : Trivialization F (TotalSpace.proj : TotalSpace F E → B)) [MemTrivializationAtlas e] (f : M → TotalSpace F E) {x₀ : M} (he : f x₀ ∈ e.source) : - MDifferentiableAt IM (IB.prod 𝓘(𝕜, F)) f x₀ ↔ - MDiffAt (fun x => (f x).proj) x₀ ∧ MDiffAt (fun x ↦ (e (f x)).2) x₀ := by + MDiffAt f x₀ ↔ MDiffAt (fun x ↦ (f x).proj) x₀ ∧ MDiffAt (fun x ↦ (e (f x)).2) x₀ := by rw [mdifferentiableAt_totalSpace] apply and_congr_right intro hf @@ -273,7 +269,7 @@ theorem mdifferentiableWithinAt_section_iff (hex₀ : b₀ ∈ e.baseSet) : MDiffAt[u] (T% s) b₀ ↔ MDiffAt[u] (fun x ↦ (e (s x)).2) b₀ := by rw [e.mdifferentiableWithinAt_totalSpace_iff IB] - · change MDifferentiableWithinAt IB IB id u b₀ ∧ _ ↔ _ + · change MDiffAt[u] (@id B) b₀ ∧ _ ↔ _ simp [mdifferentiableWithinAt_id] exact (coe_mem_source e).mpr hex₀ @@ -321,6 +317,7 @@ theorem mdifferentiable [ContMDiffVectorBundle 1 F Z I] e.MDifferentiable (I.prod 𝓘(𝕜, F)) (I.prod 𝓘(𝕜, F)) := ⟨e.contMDiffOn.mdifferentiableOn one_ne_zero, e.contMDiffOn_symm.mdifferentiableOn one_ne_zero⟩ +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias Bundle.Trivialization.mdifferentiable := mdifferentiable end @@ -458,29 +455,30 @@ lemma mdifferentiable_smul_const_section fun x₀ ↦ (hs x₀).smul_const_section lemma MDifferentiableWithinAt.sum_section {ι : Type*} {s : Finset ι} {t : ι → (x : B) → E x} - (hs : ∀ i, MDiffAt[u] (T% (t i ·)) x₀) : + (hs : ∀ i ∈ s, MDiffAt[u] (T% (t i ·)) x₀) : MDiffAt[u] (T% (fun x ↦ ∑ i ∈ s, (t i x))) x₀ := by classical induction s using Finset.induction_on with | empty => simpa using! (contMDiffWithinAt_zeroSection 𝕜 E).mdifferentiableWithinAt one_ne_zero | insert i s hi h => - simpa [Finset.sum_insert hi] using! mdifferentiableWithinAt_add_section (hs i) h + simp only [Finset.mem_insert, forall_eq_or_imp] at hs + simpa [Finset.sum_insert hi] using mdifferentiableWithinAt_add_section (hs.1) (h hs.2) lemma MDifferentiableAt.sum_section {ι : Type*} {s : Finset ι} {t : ι → (x : B) → E x} {x₀ : B} - (hs : ∀ i, MDiffAt (T% (t i ·)) x₀) : + (hs : ∀ i ∈ s, MDiffAt (T% (t i ·)) x₀) : MDiffAt (T% (fun x ↦ ∑ i ∈ s, (t i x))) x₀ := by simp_rw [← mdifferentiableWithinAt_univ] at hs ⊢ exact MDifferentiableWithinAt.sum_section hs lemma MDifferentiableOn.sum_section {ι : Type*} {s : Finset ι} {t : ι → (x : B) → E x} - (hs : ∀ i, MDiff[u] (T% (t i ·))) : + (hs : ∀ i ∈ s, MDiff[u] (T% (t i ·))) : MDiff[u] (T% (fun x ↦ ∑ i ∈ s, (t i x))) := - fun x₀ hx₀ ↦ .sum_section fun i ↦ hs i x₀ hx₀ + fun x₀ hx₀ ↦ .sum_section fun i hi ↦ hs i hi x₀ hx₀ lemma MDifferentiable.sum_section {ι : Type*} {s : Finset ι} {t : ι → (x : B) → E x} - (hs : ∀ i, MDiff (T% (t i ·))) : + (hs : ∀ i ∈ s, MDiff (T% (t i ·))) : MDiff (T% (fun x ↦ ∑ i ∈ s, (t i x))) := - fun x₀ ↦ .sum_section fun i ↦ (hs i) x₀ + fun x₀ ↦ .sum_section fun i hi ↦ (hs i) hi x₀ /-- The scalar product `ψ • s` of a differentiable function `ψ : M → 𝕜` and a section `s` of a vector bundle `V → M` is differentiable once `s` is differentiable on an open set containing @@ -513,7 +511,7 @@ lemma MDifferentiableWithinAt.sum_section_of_locallyFinite let s := {i | ((fun i ↦ {x | t i x ≠ 0}) i ∩ u').Nonempty} have := hfin.fintype have : MDiffAt[u ∩ u'] (T% (fun x ↦ ∑ i ∈ s, (t i x))) x₀ := - .sum_section fun i ↦ ((ht' i).mono inter_subset_left) + .sum_section fun i _ ↦ ((ht' i).mono inter_subset_left) apply (mdifferentiableWithinAt_inter hu').mp apply this.congr' (fun y hy ↦ ?_) inter_subset_right (mem_of_mem_nhds hu') rw [TotalSpace.mk_inj, tsum_eq_sum'] @@ -624,10 +622,9 @@ only makes sense around a point. -/ lemma MDifferentiableWithinAt.clm_apply_of_inCoordinates (hϕ : MDiffAt[s] (fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀) - (hv : MDifferentiableWithinAt IM (IB₁.prod 𝓘(𝕜, F₁)) (fun m ↦ (v m : TotalSpace F₁ E₁)) s m₀) + (hv : MDiffAt[s] (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) (hb₂ : MDiffAt[s] b₂ m₀) : - MDifferentiableWithinAt IM (IB₂.prod 𝓘(𝕜, F₂)) - (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) s m₀ := by + MDiffAt[s] (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by rw [mdifferentiableWithinAt_totalSpace] at hv ⊢ refine ⟨hb₂, ?_⟩ apply (MDifferentiableWithinAt.clm_apply hϕ hv.2).congr_of_eventuallyEq_insert @@ -658,9 +655,8 @@ in coordinates, only makes sense around a point. -/ lemma MDifferentiableAt.clm_apply_of_inCoordinates (hϕ : MDiffAt (fun m ↦ inCoordinates F₁ E₁ F₂ E₂ (b₁ m₀) (b₁ m) (b₂ m₀) (b₂ m) (ϕ m)) m₀) - (hv : MDifferentiableAt IM (IB₁.prod 𝓘(𝕜, F₁)) (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) - (hb₂ : MDiffAt b₂ m₀) : - MDifferentiableAt IM (IB₂.prod 𝓘(𝕜, F₂)) (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by + (hv : MDiffAt (fun m ↦ (v m : TotalSpace F₁ E₁)) m₀) (hb₂ : MDiffAt b₂ m₀) : + MDiffAt (fun m ↦ (ϕ m (v m) : TotalSpace F₂ E₂)) m₀ := by rw [← mdifferentiableWithinAt_univ] at hϕ hv hb₂ ⊢ exact MDifferentiableWithinAt.clm_apply_of_inCoordinates hϕ hv hb₂ @@ -680,21 +676,20 @@ variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] lemma exists_contMDiffOn_extend [(x : M) → Module 𝕜 (V x)] [VectorBundle 𝕜 F V] [ContMDiffVectorBundle k F V I] {x₀ : M} (σ₀ : V x₀) : - ∃ s ∈ 𝓝 x₀, ContMDiffOn I (I.prod 𝓘(𝕜, F)) k (T% (extend F σ₀)) s := by + ∃ s ∈ 𝓝 x₀, CMDiff[s] k (T% (extend F σ₀)) := by set t := trivializationAt F V x₀ refine ⟨t.baseSet, ?_, ?_⟩ · refine t.open_baseSet.mem_nhds ?_ exact FiberBundle.mem_baseSet_trivializationAt' x₀ - suffices ContMDiffOn I 𝓘(𝕜, F) k (fun x ↦ (t ⟨x, extend F σ₀ x⟩).2) t.baseSet by + suffices CMDiff[t.baseSet] k (fun x ↦ (t ⟨x, extend F σ₀ x⟩).2) by intro x hx rw [t.contMDiffWithinAt_section _ hx] exact this x hx let w : F := (t ⟨x₀, σ₀⟩).2 - have : ContMDiffOn I 𝓘(𝕜, F) k (fun _x ↦ w) t.baseSet := contMDiffOn_const + have : CMDiff[t.baseSet] k (fun (_x : M) ↦ w) := contMDiffOn_const exact this.congr (fun x hx ↦ by simp [extend, t, w, hx]) -lemma contMDiffAt_extend' {x : M} (σ₀ : V x) : - CMDiffAt k (T% (extend F σ₀)) x := by +lemma contMDiffAt_extend {x : M} (σ₀ : V x) : CMDiffAt k (T% (extend F σ₀)) x := by rw [contMDiffAt_section] set t := trivializationAt F V x let w : F := (t ⟨x, σ₀⟩).2 @@ -706,15 +701,17 @@ lemma contMDiffAt_extend' {x : M} (σ₀ : V x) : simp [extend, t, hx, w] · exact FiberBundle.mem_baseSet_trivializationAt' x +@[deprecated (since := "2026-06-30")] alias contMDiffAt_extend' := contMDiffAt_extend + lemma exists_mdifferentiableOn_extend [∀ x, Module 𝕜 (V x)] [VectorBundle 𝕜 F V] [ContMDiffVectorBundle 1 F V I] {x₀ : M} (σ₀ : V x₀) : - ∃ s ∈ 𝓝 x₀, MDifferentiableOn I (I.prod 𝓘(𝕜, F)) (T% (extend F σ₀)) s := by + ∃ s ∈ 𝓝 x₀, MDiff[s] (T% (extend F σ₀)) := by obtain ⟨s, hs, hsσ⟩ := exists_contMDiffOn_extend (k := 1) I F σ₀ exact ⟨s, hs, hsσ.mdifferentiableOn one_ne_zero⟩ lemma mdifferentiableAt_extend {x : M} (σ₀ : V x) : MDiffAt (T% (extend F σ₀)) x := - (contMDiffAt_extend' (k := 1) I F σ₀).mdifferentiableAt one_ne_zero + (contMDiffAt_extend (k := 1) I F σ₀).mdifferentiableAt one_ne_zero variable (V) in lemma _root_.VectorBundle.injective_eval_mdifferentiableAt_sec [∀ x, Module 𝕜 (V x)] @@ -734,7 +731,7 @@ lemma _root_.VectorBundle.injective_eval_contMDiffAt_sec {n : WithTop ℕ∞} [ fun (Z : Π x, V x) (_ : CMDiffAt n (T% Z) x) ↦ A (Z x)) := by intro X X' h ext σ₀ - simpa using congr($h (extend F σ₀) (contMDiffAt_extend' ..)) + simpa using congr($h (extend F σ₀) (contMDiffAt_extend ..)) end FiberBundle end extend diff --git a/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean b/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean index bc90f650272711..b5fc92352e7156 100644 --- a/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean +++ b/Mathlib/Geometry/Manifold/VectorBundle/Riemannian.lean @@ -119,9 +119,9 @@ variable /-- Given two smooth maps into the same fibers of a Riemannian bundle, their scalar product is smooth. -/ lemma ContMDiffWithinAt.inner_bundle - (hv : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s x) - (hw : ContMDiffWithinAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s x) : - ContMDiffWithinAt IM 𝓘(ℝ) n (fun m ↦ ⟪v m, w m⟫) s x := by + (hv : CMDiffAt[s] n (fun m ↦ (v m : TotalSpace F E)) x) + (hw : CMDiffAt[s] n (fun m ↦ (w m : TotalSpace F E)) x) : + CMDiffAt[s] n (fun m ↦ ⟪v m, w m⟫) x := by rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ have hb : CMDiffAt[s] n b x := by simp only [contMDiffWithinAt_totalSpace] at hv @@ -139,24 +139,24 @@ lemma ContMDiffWithinAt.inner_bundle /-- Given two smooth maps into the same fibers of a Riemannian bundle, their scalar product is smooth. -/ lemma ContMDiffAt.inner_bundle - (hv : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) x) - (hw : ContMDiffAt IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) x) : - ContMDiffAt IM 𝓘(ℝ) n (fun b ↦ ⟪v b, w b⟫) x := + (hv : CMDiffAt n (fun m ↦ (v m : TotalSpace F E)) x) + (hw : CMDiffAt n (fun m ↦ (w m : TotalSpace F E)) x) : + CMDiffAt n (fun b ↦ ⟪v b, w b⟫) x := ContMDiffWithinAt.inner_bundle hv hw /-- Given two smooth maps into the same fibers of a Riemannian bundle, their scalar product is smooth. -/ lemma ContMDiffOn.inner_bundle - (hv : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E)) s) - (hw : ContMDiffOn IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E)) s) : - ContMDiffOn IM 𝓘(ℝ) n (fun b ↦ ⟪v b, w b⟫) s := + (hv : CMDiff[s] n (fun m ↦ (v m : TotalSpace F E))) + (hw : CMDiff[s] n (fun m ↦ (w m : TotalSpace F E))) : + CMDiff[s] n (fun b ↦ ⟪v b, w b⟫) := fun x hx ↦ (hv x hx).inner_bundle (hw x hx) /-- Given two smooth maps into the same fibers of a Riemannian bundle, their scalar product is smooth. -/ lemma ContMDiff.inner_bundle - (hv : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (v m : TotalSpace F E))) - (hw : ContMDiff IM (IB.prod 𝓘(ℝ, F)) n (fun m ↦ (w m : TotalSpace F E))) : + (hv : CMDiff n (fun m ↦ (v m : TotalSpace F E))) + (hw : CMDiff n (fun m ↦ (w m : TotalSpace F E))) : CMDiff n (fun b ↦ ⟪v b, w b⟫) := fun x ↦ (hv x).inner_bundle (hw x) @@ -174,11 +174,11 @@ variable /-- Given two differentiable maps into the same fibers of a Riemannian bundle, their scalar product is differentiable. -/ lemma MDifferentiableWithinAt.inner_bundle - (hv : MDifferentiableWithinAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E)) s x) - (hw : MDifferentiableWithinAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E)) s x) : + (hv : MDiffAt[s] (fun m ↦ (v m : TotalSpace F E)) x) + (hw : MDiffAt[s] (fun m ↦ (w m : TotalSpace F E)) x) : MDiffAt[s] (fun m ↦ ⟪v m, w m⟫) x := by rcases h.exists_contMDiff with ⟨g, g_smooth, hg⟩ - have hb : MDifferentiableWithinAt IM IB b s x := by + have hb : MDiffAt[s] b x := by simp only [mdifferentiableWithinAt_totalSpace] at hv exact hv.1 simp only [hg] @@ -195,24 +195,24 @@ lemma MDifferentiableWithinAt.inner_bundle /-- Given two differentiable maps into the same fibers of a Riemannian bundle, their scalar product is differentiable. -/ lemma MDifferentiableAt.inner_bundle - (hv : MDifferentiableAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E)) x) - (hw : MDifferentiableAt IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E)) x) : + (hv : MDiffAt (fun m ↦ (v m : TotalSpace F E)) x) + (hw : MDiffAt (fun m ↦ (w m : TotalSpace F E)) x) : MDiffAt (fun b ↦ ⟪v b, w b⟫) x := MDifferentiableWithinAt.inner_bundle hv hw /-- Given two differentiable maps into the same fibers of a Riemannian bundle, their scalar product is differentiable. -/ lemma MDifferentiableOn.inner_bundle - (hv : MDifferentiableOn IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E)) s) - (hw : MDifferentiableOn IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E)) s) : + (hv : MDiff[s] (fun m ↦ (v m : TotalSpace F E))) + (hw : MDiff[s] (fun m ↦ (w m : TotalSpace F E))) : MDiff[s] (fun b ↦ ⟪v b, w b⟫) := fun x hx ↦ (hv x hx).inner_bundle (hw x hx) /-- Given two differentiable maps into the same fibers of a Riemannian bundle, their scalar product is differentiable. -/ lemma MDifferentiable.inner_bundle - (hv : MDifferentiable IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (v m : TotalSpace F E))) - (hw : MDifferentiable IM (IB.prod 𝓘(ℝ, F)) (fun m ↦ (w m : TotalSpace F E))) : + (hv : MDiff (fun m ↦ (v m : TotalSpace F E))) + (hw : MDiff (fun m ↦ (w m : TotalSpace F E))) : MDiff (fun b ↦ ⟪v b, w b⟫) := fun x ↦ (hv x).inner_bundle (hw x) diff --git a/Mathlib/Geometry/Manifold/VectorBundle/Tensoriality.lean b/Mathlib/Geometry/Manifold/VectorBundle/Tensoriality.lean index ea840a37f5c4d9..a4b1e190339b7c 100644 --- a/Mathlib/Geometry/Manifold/VectorBundle/Tensoriality.lean +++ b/Mathlib/Geometry/Manifold/VectorBundle/Tensoriality.lean @@ -110,7 +110,7 @@ theorem zero (hΦ : TensorialAt I F Φ x) : Φ 0 = 0 := by /-- A tensorial operation on sections of a vector bundle respects sums (since it respects binary addition). -/ theorem sum (hΦ : TensorialAt I F Φ x) {ι : Type*} {s : Finset ι} (σ : ι → Π x : M, V x) - (hσ : ∀ i, MDiffAt (T% (σ i)) x) : + (hσ : ∀ i ∈ s, MDiffAt (T% (σ i)) x) : Φ (fun x' ↦ ∑ i ∈ s, σ i x') = ∑ i ∈ s, Φ (σ i) := by classical induction s using Finset.induction_on with @@ -118,8 +118,9 @@ theorem sum (hΦ : TensorialAt I F Φ x) {ι : Type*} {s : Finset ι} (σ : ι rw [Finset.sum_empty] exact hΦ.zero | insert a s ha h => - simp only [Finset.sum_insert ha, ← h] - exact hΦ.add (hσ a) (.sum_section hσ) + simp only [Finset.mem_insert, forall_eq_or_imp] at hσ + simp only [Finset.sum_insert ha, ← h hσ.2] + exact hΦ.add (hσ.1) (.sum_section hσ.2) variable [CompleteSpace 𝕜] [FiniteDimensional 𝕜 F] [FiniteDimensional 𝕜 F'] [ContMDiffVectorBundle 1 F V I] [ContMDiffVectorBundle 1 F' V' I] @@ -147,7 +148,7 @@ lemma pointwise (hΦ : TensorialAt I F Φ x) {σ σ' : Π x : M, V x} have hΦ_eq {σ : (x : M) → V x} (hσ : MDiffAt (T% σ) x) : Φ σ = Φ (fun x' ↦ ∑ i, c i x' (σ x') • s i x') := hΦ.local hσ - (.sum_section fun i ↦ (hc hσ i).smul_section (hs i)) + (.sum_section fun i _ ↦ (hc hσ i).smul_section (hs i)) (t.eventually_eq_localFrame_sum_coeff_smul b x_mem) -- Now evaluate using the tensoriality properties. rw [hΦ_eq hσ, hΦ_eq hσ', hΦ.sum, hΦ.sum] @@ -157,8 +158,8 @@ lemma pointwise (hΦ : TensorialAt I F Φ x) {σ σ' : Π x : M, V x} _ = c i x (σ' x) • Φ (s i) := by rw [hσσ'] _ = Φ ((LinearMap.piApply (c i) σ') • (s i)) := hΦ.smul (hc hσ' i) (hs i) |>.symm - · exact fun i ↦ (hc hσ' i).smul_section (hs i) - · exact fun i ↦ (hc hσ i).smul_section (hs i) + · exact fun i _ ↦ (hc hσ' i).smul_section (hs i) + · exact fun i _ ↦ (hc hσ i).smul_section (hs i) /-- If the operation `Φ` on sections of vector bundles `V` and `V'` is tensorial at `x` in each argument, then it depends only on the value of the sections at `x`. -/ diff --git a/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean b/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean index d107d81b0c2720..40c611771099de 100644 --- a/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean +++ b/Mathlib/Geometry/Manifold/VectorField/LieBracket.lean @@ -360,7 +360,7 @@ Product rule for Lie brackets: given two vector fields `V` and `W` on `M` and a -/ lemma mlieBracketWithin_smul_right {f : M → 𝕜} (hf : MDiffAt[s] f x) (hW : MDiffAt[s] (fun x ↦ (W x : TangentBundle I M)) x) - (hs : UniqueMDiffWithinAt I s x) : + (hs : UniqueMDiffAt[s] x) : mlieBracketWithin I V (f • W) s x = d[s] f x (V x) • (W x) + (f x) • mlieBracketWithin I V W s x := by simp only [mlieBracketWithin, mpullbackWithin_smul] @@ -401,7 +401,7 @@ Product rule for Lie brackets: given two vector fields `V` and `W` on `M` and a -/ lemma mlieBracketWithin_smul_left {f : M → 𝕜} (hf : MDiffAt[s] f x) (hV : MDiffAt[s] (fun x ↦ (V x : TangentBundle I M)) x) - (hs : UniqueMDiffWithinAt I s x) : + (hs : UniqueMDiffAt[s] x) : mlieBracketWithin I (f • V) W s x = - d[s] f x (W x) • (V x) + (f x) • mlieBracketWithin I V W s x := by rw [mlieBracketWithin_swap, Pi.neg_apply, mlieBracketWithin_smul_right hf hV (V := W) hs, @@ -420,7 +420,7 @@ lemma mlieBracket_smul_left {f : M → 𝕜} (hf : MDiffAt f x) exact mlieBracketWithin_smul_left hf hV (uniqueMDiffWithinAt_univ I) lemma mlieBracketWithin_const_smul_left - (hV : MDiffAt[s] (T% V) x) (hs : UniqueMDiffWithinAt I s x) : + (hV : MDiffAt[s] (T% V) x) (hs : UniqueMDiffAt[s] x) : mlieBracketWithin I (c • V) W s x = c • mlieBracketWithin I V W s x := by simpa [mfderivWithin_const, mvfderivWithin] using! mlieBracketWithin_smul_left (mdifferentiableWithinAt_const (c := c)) (W := W) hV hs @@ -431,7 +431,7 @@ lemma mlieBracket_const_smul_left (hV : MDiffAt (T% V) x) : exact mlieBracketWithin_const_smul_left hV (uniqueMDiffWithinAt_univ _) lemma mlieBracketWithin_const_smul_right - (hW : MDiffAt[s] (T% W) x) (hs : UniqueMDiffWithinAt I s x) : + (hW : MDiffAt[s] (T% W) x) (hs : UniqueMDiffAt[s] x) : mlieBracketWithin I V (c • W) s x = c • mlieBracketWithin I V W s x := by simpa [mfderivWithin_const, mvfderivWithin] using! mlieBracketWithin_smul_right (mdifferentiableWithinAt_const (c := c)) (V := V) hW hs @@ -443,7 +443,7 @@ lemma mlieBracket_const_smul_right (hW : MDiffAt (T% W) x) : set_option backward.isDefEq.respectTransparency false in lemma mlieBracketWithin_add_left - (hV : MDiffAt[s] (T% V) x) (hV₁ : MDiffAt[s] (T% V₁) x) (hs : UniqueMDiffWithinAt I s x) : + (hV : MDiffAt[s] (T% V) x) (hV₁ : MDiffAt[s] (T% V₁) x) (hs : UniqueMDiffAt[s] x) : mlieBracketWithin I (V + V₁) W s x = mlieBracketWithin I V W s x + mlieBracketWithin I V₁ W s x := by simp only [mlieBracketWithin_apply] @@ -458,7 +458,7 @@ lemma mlieBracket_add_left (hV : MDiffAt (T% V) x) (hV₁ : MDiffAt (T% V₁) x) exact mlieBracketWithin_add_left hV hV₁ (uniqueMDiffWithinAt_univ _) lemma mlieBracketWithin_add_right - (hW : MDiffAt[s] (T% W) x) (hW₁ : MDiffAt[s] (T% W₁) x) (hs : UniqueMDiffWithinAt I s x) : + (hW : MDiffAt[s] (T% W) x) (hW₁ : MDiffAt[s] (T% W₁) x) (hs : UniqueMDiffAt[s] x) : mlieBracketWithin I V (W + W₁) s x = mlieBracketWithin I V W s x + mlieBracketWithin I V W₁ s x := by rw [mlieBracketWithin_swap, Pi.neg_apply, mlieBracketWithin_add_left hW hW₁ hs, @@ -470,7 +470,7 @@ lemma mlieBracket_add_right (hW : MDiffAt (T% W) x) (hW₁ : MDiffAt (T% W₁) x simp only [← mlieBracketWithin_univ] at hW hW₁ ⊢ exact mlieBracketWithin_add_right hW hW₁ (uniqueMDiffWithinAt_univ _) -theorem mlieBracketWithin_of_mem_nhdsWithin (st : t ∈ 𝓝[s] x) (hs : UniqueMDiffWithinAt I s x) +theorem mlieBracketWithin_of_mem_nhdsWithin (st : t ∈ 𝓝[s] x) (hs : UniqueMDiffAt[s] x) (hV : MDiffAt[t] (T% V) x) (hW : MDiffAt[t] (T% W) x) : mlieBracketWithin I V W s x = mlieBracketWithin I V W t x := by simp only [mlieBracketWithin_apply] @@ -484,12 +484,12 @@ theorem mlieBracketWithin_of_mem_nhdsWithin (st : t ∈ 𝓝[s] x) (hs : UniqueM · exact hV.differentiableWithinAt_mpullbackWithin_vectorField · exact hW.differentiableWithinAt_mpullbackWithin_vectorField -theorem mlieBracketWithin_subset (st : s ⊆ t) (ht : UniqueMDiffWithinAt I s x) +theorem mlieBracketWithin_subset (st : s ⊆ t) (ht : UniqueMDiffAt[s] x) (hV : MDiffAt[t] (T% V) x) (hW : MDiffAt[t] (T% W) x) : mlieBracketWithin I V W s x = mlieBracketWithin I V W t x := mlieBracketWithin_of_mem_nhdsWithin (nhdsWithin_mono _ st self_mem_nhdsWithin) ht hV hW -theorem mlieBracketWithin_eq_mlieBracket (hs : UniqueMDiffWithinAt I s x) +theorem mlieBracketWithin_eq_mlieBracket (hs : UniqueMDiffAt[s] x) (hV : MDiffAt (T% V) x) (hW : MDiffAt (T% W) x) : mlieBracketWithin I V W s x = mlieBracket I V W x := by simp only [← mlieBracketWithin_univ, ← mdifferentiableWithinAt_univ] at hV hW ⊢ @@ -498,7 +498,7 @@ theorem mlieBracketWithin_eq_mlieBracket (hs : UniqueMDiffWithinAt I s x) theorem _root_.DifferentiableWithinAt.mlieBracketWithin_congr_mono (hV : MDiffAt[s] (T% V) x) (hVs : EqOn V₁ V t) (hVx : V₁ x = V x) (hW : MDiffAt[s] (T% W) x) (hWs : EqOn W₁ W t) (hWx : W₁ x = W x) - (hxt : UniqueMDiffWithinAt I t x) (h₁ : t ⊆ s) : + (hxt : UniqueMDiffAt[t] x) (h₁ : t ⊆ s) : mlieBracketWithin I V₁ W₁ t x = mlieBracketWithin I V W s x := by rw [mlieBracketWithin_congr hVs hVx hWs hWx] exact mlieBracketWithin_subset h₁ hxt hV hW @@ -516,7 +516,7 @@ in chart domains. -/ private lemma mpullbackWithin_mlieBracketWithin_aux [CompleteSpace E'] {f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'} (hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀)) - (hu : UniqueMDiffOn I s) (hf : CMDiff[s] 2 f) (hx₀ : x₀ ∈ s) + (hu : UniqueMDiff[s]) (hf : CMDiff[s] 2 f) (hx₀ : x₀ ∈ s) (ht : t ⊆ (extChartAt I' (f x₀)).source) (hst : MapsTo f s t) (hsymm : IsSymmSndFDerivWithinAt 𝕜 ((extChartAt I' (f x₀)) ∘ f ∘ (extChartAt I x₀).symm) ((extChartAt I x₀).symm ⁻¹' s ∩ range I) (extChartAt I x₀ x₀)) : @@ -578,7 +578,7 @@ private lemma mpullbackWithin_mlieBracketWithin_aux [CompleteSpace E'] filter_upwards [self_mem_nhdsWithin, this] with y hy h'''y have h'y : f ((extChartAt I x₀).symm y) ∈ (extChartAt I' (f x₀)).source := ht (hst hy.1) have h''y : f ((extChartAt I x₀).symm y) ∈ (chartAt H' (f x₀)).source := by simpa using h'y - have huy : UniqueMDiffWithinAt 𝓘(𝕜, E) ((extChartAt I x₀).symm ⁻¹' s ∩ range I) y := by + have huy : UniqueMDiffAt[(extChartAt I x₀).symm ⁻¹' s ∩ range I] y := by apply UniqueDiffWithinAt.uniqueMDiffWithinAt rw [inter_comm] apply hu.uniqueDiffWithinAt_range_inter @@ -652,7 +652,7 @@ diffeomorphisms. -/ lemma mpullbackWithin_mlieBracketWithin_of_isSymmSndFDerivWithinAt {f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'} (hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀)) - (hu : UniqueMDiffOn I s) (hf : CMDiffAt[s] 2 f x₀) (hx₀ : x₀ ∈ s) + (hu : UniqueMDiff[s]) (hf : CMDiffAt[s] 2 f x₀) (hx₀ : x₀ ∈ s) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) (hsymm : IsSymmSndFDerivWithinAt 𝕜 ((extChartAt I' (f x₀)) ∘ f ∘ (extChartAt I x₀).symm) ((extChartAt I x₀).symm ⁻¹' s ∩ range I) (extChartAt I x₀ x₀)) : @@ -741,7 +741,7 @@ becomes easier to check.) -/ lemma mpullbackWithin_mlieBracketWithin' {f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s u : Set M} {t : Set M'} (hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀)) - (hs : UniqueMDiffOn I s) (hu : UniqueMDiffOn I u) + (hs : UniqueMDiff[s]) (hu : UniqueMDiff[u]) (hf : CMDiffAt[u] n f x₀) (hx₀ : x₀ ∈ s) (hn : minSmoothness 𝕜 2 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) (h'x₀ : x₀ ∈ closure (interior u)) (hsu : s ⊆ u) : mpullbackWithin I I' f (mlieBracketWithin I' V W t) s x₀ = @@ -777,7 +777,7 @@ lemma mpullbackWithin_mlieBracketWithin' lemma mpullbackWithin_mlieBracketWithin {f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'} (hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀)) - (hu : UniqueMDiffOn I s) (hf : CMDiffAt[s] n f x₀) (hx₀ : x₀ ∈ s) + (hu : UniqueMDiff[s]) (hf : CMDiffAt[s] n f x₀) (hx₀ : x₀ ∈ s) (hn : minSmoothness 𝕜 2 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) (h'x₀ : x₀ ∈ closure (interior s)) : mpullbackWithin I I' f (mlieBracketWithin I' V W t) s x₀ = @@ -788,7 +788,7 @@ lemma mpullbackWithin_mlieBracketWithin lemma mpullback_mlieBracketWithin {f : M → M'} {V W : Π (x : M'), TangentSpace I' x} {x₀ : M} {s : Set M} {t : Set M'} (hV : MDiffAt[t] (T% V) (f x₀)) (hW : MDiffAt[t] (T% W) (f x₀)) - (hu : UniqueMDiffOn I s) (hf : CMDiffAt n f x₀) (hx₀ : x₀ ∈ s) + (hu : UniqueMDiff[s]) (hf : CMDiffAt n f x₀) (hx₀ : x₀ ∈ s) (hn : minSmoothness 𝕜 2 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) : mpullback I I' f (mlieBracketWithin I' V W t) x₀ = mlieBracketWithin I (mpullback I I' f V) (mpullback I I' f W) s x₀ := by @@ -830,7 +830,7 @@ protected lemma _root_.ContMDiffWithinAt.mlieBracketWithin_vectorField [IsManifold I (n + 1) M] {m : ℕ∞ω} {U V : Π (x : M), TangentSpace I x} {s : Set M} {x : M} (hU : CMDiffAt[s] n (T% U) x) (hV : CMDiffAt[s] n (T% V) x) - (hs : UniqueMDiffOn I s) (hx : x ∈ s) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) : + (hs : UniqueMDiff[s]) (hx : x ∈ s) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) : CMDiffAt[s] m (T% (mlieBracketWithin I U V s)) x := by /- The statement is not obvious, since at different points the Lie bracket is defined using different charts. However, since we know that the Lie bracket is invariant under diffeos, we can @@ -902,7 +902,7 @@ lemma _root_.ContMDiffAt.mlieBracket_vectorField {m n : ℕ∞} lemma _root_.ContMDiffOn.mlieBracketWithin_vectorField {m n : ℕ∞} [IsManifold I (n + 1) M] {U V : Π (x : M), TangentSpace I x} (hU : CMDiff[s] n (T% U)) (hV : CMDiff[s] n (T% V)) - (hs : UniqueMDiffOn I s) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) : + (hs : UniqueMDiff[s]) (hmn : minSmoothness 𝕜 (m + 1) ≤ n) : CMDiff[s] m (T% (mlieBracketWithin I U V s)) := fun x hx ↦ (hU x hx).mlieBracketWithin_vectorField (hV x hx) hs hx hmn @@ -924,7 +924,7 @@ variable [IsManifold I (minSmoothness 𝕜 3) M] [CompleteSpace E] `[U, [V, W]] = [[U, V], W] + [V, [U, W]]` (also called Jacobi identity). -/ theorem leibniz_identity_mlieBracketWithin_apply {U V W : Π (x : M), TangentSpace I x} {s : Set M} {x : M} - (hs : UniqueMDiffOn I s) (h's : x ∈ closure (interior s)) (hx : x ∈ s) + (hs : UniqueMDiff[s]) (h's : x ∈ closure (interior s)) (hx : x ∈ s) (hU : CMDiffAt[s] (minSmoothness 𝕜 2) (T% U) x) (hV : CMDiffAt[s] (minSmoothness 𝕜 2) (T% V) x) (hW : CMDiffAt[s] (minSmoothness 𝕜 2) (T% W) x) : diff --git a/Mathlib/Geometry/Manifold/VectorField/Pullback.lean b/Mathlib/Geometry/Manifold/VectorField/Pullback.lean index 3510738005c08e..7a44b80b89f725 100644 --- a/Mathlib/Geometry/Manifold/VectorField/Pullback.lean +++ b/Mathlib/Geometry/Manifold/VectorField/Pullback.lean @@ -154,7 +154,7 @@ lemma mpullbackWithin_neg : simp [mpullbackWithin_apply] set_option backward.isDefEq.respectTransparency false in -lemma mpullbackWithin_id {V : Π (x : M), TangentSpace I x} (h : UniqueMDiffWithinAt I s x) : +lemma mpullbackWithin_id {V : Π (x : M), TangentSpace I x} (h : UniqueMDiffAt[s] x) : mpullbackWithin I I id V s x = V x := by simp [mpullbackWithin_apply, mfderivWithin_id h] @@ -222,7 +222,7 @@ set_option backward.isDefEq.respectTransparency false in lemma mpullbackWithin_comp_of_left {g : M' → M''} {f : M → M'} {V : Π (x : M''), TangentSpace I'' x} {s : Set M} {t : Set M'} {x₀ : M} (hf : MDiffAt[s] f x₀) (h : Set.MapsTo f s t) - (hu : UniqueMDiffWithinAt I s x₀) (hg' : (mfderiv[t] g (f x₀)).IsInvertible) : + (hu : UniqueMDiffAt[s] x₀) (hg' : (mfderiv[t] g (f x₀)).IsInvertible) : mpullbackWithin I I'' (g ∘ f) V s x₀ = mpullbackWithin I I' f (mpullbackWithin I' I'' g V t) s x₀ := by simp only [mpullbackWithin] @@ -235,7 +235,7 @@ set_option backward.isDefEq.respectTransparency false in lemma mpullbackWithin_comp_of_right {g : M' → M''} {f : M → M'} {V : Π (x : M''), TangentSpace I'' x} {s : Set M} {t : Set M'} {x₀ : M} (hg : MDiffAt[t] g (f x₀)) (h : Set.MapsTo f s t) - (hu : UniqueMDiffWithinAt I s x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) : + (hu : UniqueMDiffAt[s] x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) : mpullbackWithin I I'' (g ∘ f) V s x₀ = mpullbackWithin I I' f (mpullbackWithin I' I'' g V t) s x₀ := by simp only [mpullbackWithin] @@ -260,7 +260,7 @@ variable [IsManifold I 2 M] [IsManifold I' 2 M'] [CompleteSpace E] differentiable. Version within a set at a point. -/ protected lemma _root_.MDifferentiableWithinAt.mpullbackWithin_vectorField_inter (hV : MDiffAt[t] (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : 2 ≤ n) : + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : 2 ≤ n) : MDiffAt[s ∩ f ⁻¹' t] (T% (mpullbackWithin I I' f V s)) x₀ := by /- We want to apply the theorem `MDifferentiableWithinAt.clm_apply_of_inCoordinates`, stating that applying linear maps to vector fields gives a smooth result when the linear map and @@ -324,7 +324,7 @@ protected lemma _root_.MDifferentiableWithinAt.mpullbackWithin_vectorField_inter lemma _root_.MDifferentiableWithinAt.mpullbackWithin_vectorField_inter_of_eq (hV : MDiffAt[t] (T% V) y₀) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : 2 ≤ n) (h : y₀ = f x₀) : + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : 2 ≤ n) (h : y₀ = f x₀) : MDiffAt[s ∩ f ⁻¹' t] (T% (mpullbackWithin I I' f V s)) x₀ := by subst h exact hV.mpullbackWithin_vectorField_inter hf hf' hx₀ hs hmn @@ -334,7 +334,7 @@ differentiable. Version on a set. -/ protected lemma _root_.MDifferentiableOn.mpullbackWithin_vectorField_inter (hV : MDiff[t] (T% V)) (hf : CMDiff[s] n f) (hf' : ∀ x ∈ s ∩ f ⁻¹' t, (mfderiv[s] f x).IsInvertible) - (hs : UniqueMDiffOn I s) (hmn : 2 ≤ n) : + (hs : UniqueMDiff[s]) (hmn : 2 ≤ n) : MDiff[(s ∩ f ⁻¹' t)] (T% (mpullbackWithin I I' f V s)) := fun _ hx₀ ↦ MDifferentiableWithinAt.mpullbackWithin_vectorField_inter (hV _ hx₀.2) (hf _ hx₀.1) (hf' _ hx₀) hx₀.1 hs hmn @@ -395,7 +395,7 @@ Version within a set at a point. -/ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_inter (hV : CMDiffAt[t] m (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) : + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) : CMDiffAt[s ∩ f ⁻¹' t] m (T% (mpullbackWithin I I' f V s)) x₀ := by /- We want to apply the theorem `ContMDiffWithinAt.clm_apply_of_inCoordinates`, stating that applying linear maps to vector fields gives a smooth result when the linear map and the @@ -454,7 +454,7 @@ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_inter lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_inter_of_eq (hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (h : f x₀ = y₀) : + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (h : f x₀ = y₀) : CMDiffAt[s ∩ f ⁻¹' t] m (T% (mpullbackWithin I I' f V s)) x₀ := by subst h exact ContMDiffWithinAt.mpullbackWithin_vectorField_inter hV hf hf' hx₀ hs hmn @@ -465,7 +465,7 @@ Version within a set at a point. -/ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_mem (hV : CMDiffAt[t] m (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) : + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) : CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ := by apply (ContMDiffWithinAt.mpullbackWithin_vectorField_inter hV hf hf' hx₀ hs hmn).mono_of_mem_nhdsWithin @@ -476,7 +476,7 @@ with `m + 1 ≤ n` is `C^m`. Version within a set at a point. -/ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_mem_of_eq (hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) (hy₀ : f x₀ = y₀) : CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ := by subst hy₀ @@ -488,7 +488,7 @@ Version within a set at a point. -/ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField (hV : CMDiffAt[t] m (T% V) (f x₀)) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) : + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) : CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ := ContMDiffWithinAt.mpullbackWithin_vectorField_of_mem hV hf hf' hx₀ hs hmn hst.preimage_mem_nhdsWithin @@ -498,7 +498,7 @@ with `m + 1 ≤ n` is `C^m`. Version within a set at a point. -/ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_eq (hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[s] n f x₀) (hf' : (mfderiv[s] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) (h : f x₀ = y₀) : + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : MapsTo f s t) (h : f x₀ = y₀) : CMDiffAt[s] m (T% (mpullbackWithin I I' f V s)) x₀ := by subst h exact ContMDiffWithinAt.mpullbackWithin_vectorField hV hf hf' hx₀ hs hmn hst @@ -509,7 +509,7 @@ Version within a set at a point, with a set used for the pullback possibly large protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField' {u : Set M} (hV : CMDiffAt[t] m (T% V) (f x₀)) (hf : CMDiffAt[u] n f x₀) (hf' : (mfderiv[u] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) (hu : s ⊆ u) : CMDiffAt[s] m (T% (mpullbackWithin I I' f V u)) x₀ := by have hn : 1 ≤ n := le_trans (by simp) hmn @@ -530,7 +530,7 @@ with `m + 1 ≤ n` is `C^m`. Version within a set at a point, with a set used for the pullback possibly larger. -/ protected lemma _root_.ContMDiffWithinAt.mpullbackWithin_vectorField_of_eq' {u : Set M} (hV : CMDiffAt[t] m (T% V) y₀) (hf : CMDiffAt[u] n f x₀) (hf' : (mfderiv[u] f x₀).IsInvertible) - (hx₀ : x₀ ∈ s) (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) + (hx₀ : x₀ ∈ s) (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) (hst : f ⁻¹' t ∈ 𝓝[s] x₀) (hu : s ⊆ u) (hy₀ : f x₀ = y₀) : CMDiffAt[s] m (T% (mpullbackWithin I I' f V u)) x₀ := by subst hy₀ @@ -542,7 +542,7 @@ Version on a set. -/ protected lemma _root_.ContMDiffOn.mpullbackWithin_vectorField_inter (hV : CMDiff[t] m (T% V)) (hf : CMDiff[s] n f) (hf' : ∀ x ∈ s ∩ f ⁻¹' t, (mfderiv[s] f x).IsInvertible) - (hs : UniqueMDiffOn I s) (hmn : m + 1 ≤ n) : + (hs : UniqueMDiff[s]) (hmn : m + 1 ≤ n) : CMDiff[s ∩ f ⁻¹' t] m (T% (mpullbackWithin I I' f V s)) := fun _ hx₀ ↦ ContMDiffWithinAt.mpullbackWithin_vectorField_inter (hV _ hx₀.2) (hf _ hx₀.1) (hf' _ hx₀) hx₀.1 hs hmn @@ -616,7 +616,7 @@ protected lemma _root_.ContMDiff.mpullback_vectorField lemma contMDiffWithinAt_mpullbackWithin_extChartAt_symm {V : Π (x : M), TangentSpace I x} (hV : CMDiffAt[s] m (T% V) x) - (hs : UniqueMDiffOn I s) (hx : x ∈ s) (hmn : m + 1 ≤ n) : + (hs : UniqueMDiff[s]) (hx : x ∈ s) (hmn : m + 1 ≤ n) : CMDiffAt[(extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s] m (T% (mpullbackWithin 𝓘(𝕜, E) I (extChartAt I x).symm V (range I))) (extChartAt I x x) := ContMDiffWithinAt.mpullbackWithin_vectorField_of_eq' hV @@ -628,7 +628,7 @@ lemma contMDiffWithinAt_mpullbackWithin_extChartAt_symm lemma eventually_contMDiffWithinAt_mpullbackWithin_extChartAt_symm {V : Π (x : M), TangentSpace I x} (hV : CMDiffAt[s] m (T% V) x) - (hs : UniqueMDiffOn I s) (hx : x ∈ s) (hmn : m + 1 ≤ n) (hm : m ≠ ∞) : + (hs : UniqueMDiff[s]) (hx : x ∈ s) (hmn : m + 1 ≤ n) (hm : m ≠ ∞) : ∀ᶠ y in 𝓝[s] x, CMDiffAt[(extChartAt I x).target ∩ (extChartAt I x).symm ⁻¹' s] m (T% (mpullbackWithin 𝓘(𝕜, E) I (extChartAt I x).symm V (range I))) (extChartAt I x y) := by have T := nhdsWithin_mono _ (subset_insert _ _) diff --git a/Mathlib/Geometry/RingedSpace/Basic.lean b/Mathlib/Geometry/RingedSpace/Basic.lean index ce4c5879fe072e..9e998ab3534122 100644 --- a/Mathlib/Geometry/RingedSpace/Basic.lean +++ b/Mathlib/Geometry/RingedSpace/Basic.lean @@ -38,8 +38,9 @@ open TopCat.Presheaf namespace AlgebraicGeometry +-- The universes appear together in the type, but separately in the value. +set_option linter.checkUnivs false in /-- The type of Ringed spaces, as an abbreviation for `SheafedSpace CommRingCat`. -/ -@[nolint checkUnivs] -- The universes appear together in the type, but separately in the value. abbrev RingedSpace : Type max (u + 1) (v + 1) := SheafedSpace.{v + 1, v, u} CommRingCat.{v} diff --git a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean index be2bf1ffbab2ff..b5d34121cfec62 100644 --- a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean +++ b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace.lean @@ -84,7 +84,7 @@ structure Hom (X Y : LocallyRingedSpace.{u}) : Type _ abbrev Hom.toShHom {X Y : LocallyRingedSpace.{u}} (f : X.Hom Y) : X.toSheafedSpace ⟶ Y.toSheafedSpace := InducedCategory.homMk f.1 -@[simp, nolint simpVarHead] +@[simp] lemma Hom.toShHom_mk {X Y : LocallyRingedSpace.{u}} (f : X.toPresheafedSpace.Hom Y.toPresheafedSpace) (hf) : Hom.toShHom ⟨f, hf⟩ = InducedCategory.homMk f := rfl @@ -99,7 +99,7 @@ instance : Quiver LocallyRingedSpace := /-- A morphism of locally ringed spaces `f : X ⟶ Y` induces a local ring homomorphism from `Y.stalk (f x)` to `X.stalk x` for any `x : X`. -/ -noncomputable def Hom.stalkMap {X Y : LocallyRingedSpace.{u}} (f : Hom X Y) (x : X) : +noncomputable def Hom.stalkMap {X Y : LocallyRingedSpace.{u}} (f : X ⟶ Y) (x : X) : Y.presheaf.stalk (f.1.1 x) ⟶ X.presheaf.stalk x := f.toShHom.hom.stalkMap x diff --git a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean index 07e65e02bcb462..4175f420b82bb0 100644 --- a/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean +++ b/Mathlib/Geometry/RingedSpace/LocallyRingedSpace/ResidueField.lean @@ -48,6 +48,16 @@ def residueField (x : X) : CommRingCat := instance (x : X) : Field (X.residueField x) := inferInstanceAs <| Field (IsLocalRing.ResidueField (X.presheaf.stalk x)) +/-- The residue map from the stalk to the residue field. -/ +def residue (X : LocallyRingedSpace.{u}) (x : X) : X.presheaf.stalk x ⟶ X.residueField x := + CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk x)) + +lemma residue_surjective (x : X) : Function.Surjective (X.residue x) := + Ideal.Quotient.mk_surjective + +instance (x : X) : Epi (X.residue x) := + ConcreteCategory.epi_of_surjective _ (X.residue_surjective x) + /-- If `U` is an open of `X` containing `x`, we have a canonical ring map from the sections over `U` to the residue field of `x`. @@ -56,9 +66,7 @@ If we interpret sections over `U` as functions of `X` defined on `U`, then this corresponds to evaluation at `x`. -/ def evaluation (x : U) : X.presheaf.obj (op U) ⟶ X.residueField x := - -- TODO: make a new definition wrapping - -- `CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk _))`? - X.presheaf.germ U x.1 x.2 ≫ CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk _)) + X.presheaf.germ U x.1 x.2 ≫ X.residue _ /-- The global evaluation map from `Γ(X, ⊤)` to the residue field at `x`. -/ def Γevaluation (x : X) : X.presheaf.obj (op ⊤) ⟶ X.residueField x := @@ -97,10 +105,9 @@ a morphism of residue fields in the other direction. -/ def residueFieldMap (x : X) : Y.residueField (f.base x) ⟶ X.residueField x := CommRingCat.ofHom (IsLocalRing.ResidueField.map (f.stalkMap x).hom) +@[reassoc] lemma residue_comp_residueFieldMap_eq_stalkMap_comp_residue (x : X) : - CommRingCat.ofHom (IsLocalRing.residue (Y.presheaf.stalk (f.base x))) ≫ - residueFieldMap f x = f.stalkMap x ≫ - CommRingCat.ofHom (IsLocalRing.residue (X.presheaf.stalk x)) := by + Y.residue _ ≫ residueFieldMap f x = f.stalkMap x ≫ X.residue _ := by simp [residueFieldMap] rfl diff --git a/Mathlib/GroupTheory/Commutator/Basic.lean b/Mathlib/GroupTheory/Commutator/Basic.lean index c2602aad4ef00e..bae1b8b7e53cd9 100644 --- a/Mathlib/GroupTheory/Commutator/Basic.lean +++ b/Mathlib/GroupTheory/Commutator/Basic.lean @@ -183,6 +183,28 @@ theorem commutator_comm_le : ⁅H₁, H₂⁆ ≤ ⁅H₂, H₁⁆ := theorem commutator_comm : ⁅H₁, H₂⁆ = ⁅H₂, H₁⁆ := le_antisymm (commutator_comm_le H₁ H₂) (commutator_comm_le H₂ H₁) +@[to_additive] +theorem commutator_self_eq_bot_iff : ⁅H, H⁆ = ⊥ ↔ IsMulCommutative H := by + rw [commutator_eq_bot_iff_le_centralizer, le_centralizer_iff_isMulCommutative] + +@[to_additive (attr := simp)] +theorem commutator_top_right_eq_bot_iff_le_center : ⁅H, (⊤ : Subgroup G)⁆ = ⊥ ↔ H ≤ center G := by + rw [commutator_eq_bot_iff_le_centralizer, coe_top, centralizer_univ] + +@[to_additive (attr := simp)] +theorem commutator_top_left_eq_bot_iff_le_center : ⁅(⊤ : Subgroup G), H⁆ = ⊥ ↔ H ≤ center G := by + rw [commutator_comm, commutator_top_right_eq_bot_iff_le_center] + +variable (H) in +@[to_additive (attr := simp)] +theorem commutator_center_right : ⁅H, center G⁆ = ⊥ := by + simp [commutator_eq_bot_iff_le_centralizer] + +variable (H) in +@[to_additive (attr := simp)] +theorem commutator_center_left : ⁅center G, H⁆ = ⊥ := + commutator_eq_bot_iff_le_centralizer.mpr <| center_le_centralizer _ + section Normal @[to_additive] diff --git a/Mathlib/GroupTheory/CommutingProbability.lean b/Mathlib/GroupTheory/CommutingProbability.lean index d77c56889a52fb..bb35843efbde61 100644 --- a/Mathlib/GroupTheory/CommutingProbability.lean +++ b/Mathlib/GroupTheory/CommutingProbability.lean @@ -13,6 +13,7 @@ public import Mathlib.Tactic.Qify /-! # Commuting Probability + This file introduces the commuting probability of finite groups. ## Main definitions diff --git a/Mathlib/GroupTheory/Congruence/Basic.lean b/Mathlib/GroupTheory/Congruence/Basic.lean index 407821655bdedd..ef9728e074efa5 100644 --- a/Mathlib/GroupTheory/Congruence/Basic.lean +++ b/Mathlib/GroupTheory/Congruence/Basic.lean @@ -63,60 +63,49 @@ def pi {ι : Type*} {f : ι → Type*} [∀ i, Mul (f i)] (C : ∀ i, Con (f i)) { @piSetoid _ _ fun i => (C i).toSetoid with mul' := fun h1 h2 i => (C i).mul (h1 i) (h2 i) } -/-- Makes an isomorphism of quotients by two congruence relations, given that the relations are -equal. -/ -@[to_additive /-- Makes an additive isomorphism of quotients by two additive congruence relations, -given that the relations are equal. -/] -protected def congr {c d : Con M} (h : c = d) : c.Quotient ≃* d.Quotient := - { Quotient.congr (Equiv.refl M) <| by apply Con.ext_iff.mp h with - map_mul' := fun x y => by rcases x with ⟨⟩; rcases y with ⟨⟩; rfl } +/-- A multiplicative equivalence `e : α ≃* β` generates an equivalence between quotient spaces, +if it is compatible with the relations. -/ +@[to_additive +/-- An additive equivalence `e : α ≃+ β` generates an equivalence between quotient spaces, +if it is compatible with the relations. -/] +protected def congr {c : Con M} {d : Con N} (e : M ≃* N) (h : c = d.comap e (map_mul e)) : + c.Quotient ≃* d.Quotient where + __ := Quotient.congr e <| by apply Con.ext_iff.mp h + map_mul' := by rintro ⟨x⟩ ⟨y⟩; exact congrArg toQuotient (e.map_mul x y) @[to_additive (attr := simp)] -theorem congr_mk {c d : Con M} (h : c = d) (a : M) : - Con.congr h (a : c.Quotient) = (a : d.Quotient) := rfl +theorem congr_mk {c : Con M} {d : Con N} (e : M ≃* N) (h : c = d.comap e (map_mul e)) (a : M) : + Con.congr e h (a : c.Quotient) = (e a : d.Quotient) := rfl -@[to_additive] -theorem le_comap_conGen {M N : Type*} [Mul M] [Mul N] (f : M → N) - (H : ∀ (x y : M), f (x * y) = f x * f y) (rel : N → N → Prop) : - conGen (fun x y ↦ rel (f x) (f y)) ≤ Con.comap f H (conGen rel) := by - intro x y h - simp only [Con.comap_rel] - exact .rec (fun x y h ↦ .of (f x) (f y) h) (fun x ↦ .refl (f x)) - (fun _ h ↦ .symm h) (fun _ _ h1 h2 ↦ h1.trans h2) (fun {w x y z} _ _ h1 h2 ↦ - (congrArg (fun a ↦ conGen rel a (f (x * z))) (H w y)).mpr - (((congrArg (fun a ↦ conGen rel (f w * f y) a) (H x z))).mpr - (.mul h1 h2))) h +@[to_additive (attr := simp)] +theorem congr_symm {c : Con M} {d : Con N} (e : M ≃* N) (h : c = d.comap e (map_mul e)) : + (Con.congr e h).symm = + Con.congr e.symm (ext <| e.surjective.forall₂.2 <| by simp [h]) := + rfl @[to_additive] theorem comap_conGen_equiv {M N : Type*} [Mul M] [Mul N] (f : MulEquiv M N) (rel : N → N → Prop) : Con.comap f (map_mul f) (conGen rel) = conGen (fun x y ↦ rel (f x) (f y)) := by - apply le_antisymm _ (le_comap_conGen f (map_mul f) rel) + apply le_antisymm _ (le_comap_conGen rel f (map_mul f)) intro a b h simp only [Con.comap_rel] at h - have H : ∀ n1 n2, (conGen rel) n1 n2 → ∀ a b, f a = n1 → f b = n2 → - (conGen fun x y ↦ rel (f x) (f y)) a b := by - intro n1 n2 h - induction h with - | of x y h => - intro _ _ fa fb - apply ConGen.Rel.of - rwa [fa, fb] - | refl x => - intro _ _ fc fd - rw [f.injective (fc.trans fd.symm)] - exact ConGen.Rel.refl _ - | symm _ h => exact fun a b fs fb ↦ ConGen.Rel.symm (h b a fb fs) - | trans _ _ ih ih1 => - exact fun a b fa fb ↦ Exists.casesOn (f.surjective _) fun c' hc' ↦ - ConGen.Rel.trans (ih a c' fa hc') (ih1 c' b hc' fb) - | mul _ _ ih ih1 => - rename_i w x y z _ _ - intro a b fa fb - rw [← f.eq_symm_apply, map_mul] at fa fb - rw [fa, fb] - exact ConGen.Rel.mul (ih (f.symm w) (f.symm x) (by simp) (by simp)) - (ih1 (f.symm y) (f.symm z) (by simp) (by simp)) - exact H (f a) (f b) h a b (refl _) (refl _) + unfold Function.onFun + generalize fa : f a = n1 at h + generalize fb : f b = n2 at h + induction h generalizing a b with + | of x y h => + apply ConGen.Rel.of + rwa [fa, fb] + | refl x => + rw [f.injective (fa.trans fb.symm)] + exact ConGen.Rel.refl _ + | symm _ h => exact ConGen.Rel.symm (h fb fa) + | trans _ _ ih ih1 => + exact Exists.casesOn (f.surjective _) fun c' hc' ↦ ConGen.Rel.trans (ih fa hc') (ih1 hc' fb) + | @mul w x y z _ _ ih ih1 => + rw [← f.eq_symm_apply, map_mul] at fa fb + rw [fa, fb] + exact ConGen.Rel.mul (ih (by simp) (by simp)) (ih1 (by simp) (by simp)) @[to_additive] theorem comap_conGen_of_bijective {M N : Type*} [Mul M] [Mul N] (f : M → N) @@ -239,7 +228,7 @@ noncomputable def quotientKerEquivOfSurjective (f : M →* P) (hf : Surjective f AddCon N -/] noncomputable def comapQuotientEquivOfSurj (c : Con M) (f : N →* M) (hf : Function.Surjective f) : (Con.comap f f.map_mul c).Quotient ≃* c.Quotient := - (Con.congr Con.comap_eq).trans <| Con.quotientKerEquivOfSurjective (c.mk'.comp f) <| + (Con.congr (.refl _) Con.comap_eq).trans <| Con.quotientKerEquivOfSurjective (c.mk'.comp f) <| Con.mk'_surjective.comp hf @[to_additive (attr := simp)] @@ -263,7 +252,7 @@ lemma comapQuotientEquivOfSurj_symm_mk' (c : Con M) (f : N ≃* M) (x : N) : @[to_additive /-- The second isomorphism theorem for `AddMonoid`s. -/] noncomputable def comapQuotientEquiv (f : N →* M) : (comap f f.map_mul c).Quotient ≃* MonoidHom.mrange (c.mk'.comp f) := - (Con.congr comap_eq).trans <| quotientKerEquivRange <| c.mk'.comp f + (Con.congr (.refl _) comap_eq).trans <| quotientKerEquivRange <| c.mk'.comp f /-- The **third isomorphism theorem for monoids**. -/ @[to_additive /-- The third isomorphism theorem for `AddMonoid`s. -/] diff --git a/Mathlib/GroupTheory/Congruence/Defs.lean b/Mathlib/GroupTheory/Congruence/Defs.lean index b47a3672e90a92..499a5e8086a810 100644 --- a/Mathlib/GroupTheory/Congruence/Defs.lean +++ b/Mathlib/GroupTheory/Congruence/Defs.lean @@ -517,6 +517,23 @@ theorem comap_rel {f : M → N} (H : ∀ x y, f (x * y) = f x * f y) {c : Con N} comap f H c x y ↔ c (f x) (f y) := Iff.rfl +@[to_additive (attr := simp)] +theorem comap_id (c : Con M) : c.comap id (by intros; rfl) = c := rfl + +@[to_additive (attr := simp)] +theorem comap_comp (c : Con P) (g : N → P) (f : M → N) (hg) (hf) : + c.comap (g ∘ f) (by grind) = (c.comap g hg).comap f hf := rfl + +@[to_additive] +theorem le_comap_conGen (r : N → N → Prop) (f : M → N) (hf) : + conGen (r.onFun f) ≤ (conGen r).comap f hf := + conGen_le.2 fun _ _ h => ConGen.Rel.of _ _ h + +@[to_additive] +theorem comap_injective (f : M → N) (hf : Function.Surjective f) (hf') : + Function.Injective (comap f hf') := + .of_comp (f := toSetoid) <| (Setoid.comap_injective f hf).comp toSetoid_injective + end section diff --git a/Mathlib/GroupTheory/Congruence/Star.lean b/Mathlib/GroupTheory/Congruence/Star.lean new file mode 100644 index 00000000000000..dc4db4b4432140 --- /dev/null +++ b/Mathlib/GroupTheory/Congruence/Star.lean @@ -0,0 +1,53 @@ +/- +Copyright (c) 2020 Eric Wieser. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Eric Wieser +-/ +module + +public import Mathlib.GroupTheory.Congruence.Basic +public import Mathlib.Algebra.Star.Basic + +/-! +# Helpers for working with star operators on quotients. + +TODO: consider defining `Star` versions of `Con` and `AddCon`. +-/ + +@[expose] public section + +section Mul +variable {M : Type*} [Mul M] [StarMul M] {r : M → M → Prop} + +theorem ConGen.Rel.star (hr : ∀ a b, r a b → r (star a) (star b)) + ⦃a b : M⦄ : Rel r a b → Rel r (star a) (star b) + | refl _ => .refl _ + | symm h => .symm <| h.star hr + | trans h1 h2 => .trans (h1.star hr) (h2.star hr) + | of _ _ h => .of _ _ (hr _ _ h) + | mul h1 h2 => by + rw [star_mul, star_mul] + exact (h2.star hr).mul (h1.star hr) + +theorem conGen_star (hr : ∀ a b, r a b → r (star a) (star b)) ⦃a b : M⦄ : + conGen r a b → conGen r (star a) (star b) := (ConGen.Rel.star hr ·) + +end Mul + +section Add +variable {A : Type*} [AddMonoid A] [StarAddMonoid A] {r : A → A → Prop} + +theorem AddConGen.Rel.star (hr : ∀ a b, r a b → r (star a) (star b)) + ⦃a b : A⦄ : Rel r a b → Rel r (star a) (star b) + | refl _ => .refl _ + | symm h => .symm <| h.star hr + | trans h1 h2 => .trans (h1.star hr) (h2.star hr) + | of _ _ h => .of _ _ (hr _ _ h) + | add h1 h2 => by + rw [star_add, star_add] + exact (h1.star hr).add (h2.star hr) + +theorem addConGen_star (hr : ∀ a b, r a b → r (star a) (star b)) ⦃a b : A⦄ : + addConGen r a b → addConGen r (star a) (star b) := (AddConGen.Rel.star hr ·) + +end Add diff --git a/Mathlib/GroupTheory/FiniteAbelian/Duality.lean b/Mathlib/GroupTheory/FiniteAbelian/Duality.lean index 567965b42e562e..cb0f5de5185b7b 100644 --- a/Mathlib/GroupTheory/FiniteAbelian/Duality.lean +++ b/Mathlib/GroupTheory/FiniteAbelian/Duality.lean @@ -70,7 +70,7 @@ theorem exists_apply_ne_one_of_hasEnoughRootsOfUnity {a : G} (ha : a ≠ 1) : variable {M} in @[simp] - theorem forall_apply_eq_apply_iff {g g' : G} : +theorem forall_apply_eq_apply_iff {g g' : G} : (∀ φ : G →* Mˣ, φ g = φ g') ↔ g = g' := by refine ⟨fun h ↦ ?_, fun h ↦ by simp [h]⟩ simpa [← not_forall, not_imp_not, mul_inv_eq_one, h] using diff --git a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean index 81cbeaebab1630..c72f4d89cfe415 100644 --- a/Mathlib/GroupTheory/FinitelyPresentedGroup.lean +++ b/Mathlib/GroupTheory/FinitelyPresentedGroup.lean @@ -8,7 +8,12 @@ module public import Mathlib.Algebra.Group.Subgroup.Basic public import Mathlib.Data.Set.Finite.Basic +public import Mathlib.Data.Finite.Sum public import Mathlib.GroupTheory.FreeGroup.Basic +public import Mathlib.GroupTheory.Coprod.Basic +public import Mathlib.GroupTheory.PresentedGroup +public import Mathlib.GroupTheory.QuotientGroup.Basic +public import Mathlib.Logic.Equiv.Fin.Basic /-! # Finitely Presented Groups @@ -93,11 +98,15 @@ namespace Group.IsFinitelyPresented /-- Finitely presented groups are closed under isomorphism. -/ @[to_additive /-- Finitely presented additive groups are closed under additive isomorphism. -/ ] -theorem equiv (iso : G ≃* H) (h : IsFinitelyPresented G) : IsFinitelyPresented H := by +theorem equiv (iso : G ≃* H) [h : IsFinitelyPresented G] : IsFinitelyPresented H := by obtain ⟨n, φ, hφsurj, hNC⟩ := h refine ⟨n, (iso : G →* H).comp φ, iso.surjective.comp hφsurj, ?_⟩ rwa [φ.ker_mulEquiv_comp iso] +/-- The image of a finitely presented group under a surjective homomorphism whose kernel is +finitely generated as a normal subgroup is finitely presented. -/ +@[to_additive /-- The image of a finitely presented additive group under a surjective additive +homomorphism whose kernel is finitely generated as a normal subgroup is finitely presented. -/] theorem of_surjective [hG : IsFinitelyPresented G] (f : G →* H) (hf_surj : Function.Surjective f) (hf_ker : f.ker.IsNormalClosureFG) : IsFinitelyPresented H := by @@ -106,6 +115,21 @@ theorem of_surjective [hG : IsFinitelyPresented G] (f : G →* H) rw [← MonoidHom.comap_ker] exact hf_ker.comap hφ_surj hφ_ker +/-- The quotient of a finitely presented group by a subgroup +which is finitely generated as a normal subgroup is finitely presented. -/ +@[to_additive /-- The quotient of a finitely presented additive group by an additive subgroup +which is finitely generated as a normal subgroup is finitely presented. -/] +theorem quotient [hG : IsFinitelyPresented G] (N : Subgroup G) [N.Normal] + (hN : N.IsNormalClosureFG) : IsFinitelyPresented (G ⧸ N) := + of_surjective (QuotientGroup.mk' N) (QuotientGroup.mk'_surjective N) + ((QuotientGroup.ker_mk' N).symm ▸ hN) + +open QuotientGroup in +theorem exists_mulEquiv_presentedGroup [hg : IsFinitelyPresented G] : + ∃ n : ℕ, ∃ s : Set (FreeGroup (Fin n)), Set.Finite s ∧ Nonempty (G ≃* PresentedGroup s) := by + obtain ⟨n, φ, hφ, s, hs, hsφ⟩ := hg + exact ⟨n, s, hs, ⟨(quotientKerEquivOfSurjective φ hφ).symm.trans (quotientMulEquivOfEq hsφ.symm)⟩⟩ + /-- A free group with a finite number of generators is finitely presented. -/ @[to_additive /-- A free additive group with a finite number of generators is finitely presented. -/ ] @@ -115,13 +139,24 @@ instance [Finite α] : IsFinitelyPresented (FreeGroup α) := by · rw [(FreeGroup.map f).ker_eq_bot (FreeGroup.map_injective hf_inj.injective)] exact .bot +instance [Finite α] (s : Set (FreeGroup α)) [Finite s] : + IsFinitelyPresented (PresentedGroup s) := + of_surjective (PresentedGroup.mk s) (PresentedGroup.mk_surjective s) + ⟨s, ‹_›, (QuotientGroup.ker_mk' (Subgroup.normalClosure s)).symm⟩ + /-- `Multiplicative ℤ` is finitely presented. -/ instance : IsFinitelyPresented (Multiplicative ℤ) := - equiv (FreeGroup.mulEquivIntOfUnique : FreeGroup Unit ≃* Multiplicative ℤ) inferInstance + equiv (FreeGroup.mulEquivIntOfUnique : FreeGroup Unit ≃* Multiplicative ℤ) /-- ℤ is finitely presented -/ instance : AddGroup.IsFinitelyPresented ℤ := - AddGroup.IsFinitelyPresented.equiv - (FreeAddGroup.addEquivIntOfUnique : FreeAddGroup Unit ≃+ ℤ) inferInstance + AddGroup.IsFinitelyPresented.equiv (FreeAddGroup.addEquivIntOfUnique : FreeAddGroup Unit ≃+ ℤ) + +/-- The free product of finitely presented groups is finitely presented -/ +instance [IsFinitelyPresented G] [IsFinitelyPresented H] : + IsFinitelyPresented (Monoid.Coprod G H) := by + obtain ⟨_, sG, ⟨_ : Finite sG, ⟨φG⟩⟩⟩ := exists_mulEquiv_presentedGroup (G := G) + obtain ⟨_, sH, ⟨_ : Finite sH, ⟨φH⟩⟩⟩ := exists_mulEquiv_presentedGroup (G := H) + exact equiv ((PresentedGroup.coprodPresentations sG sH).trans (MulEquiv.coprodCongr φG φH).symm) end Group.IsFinitelyPresented diff --git a/Mathlib/GroupTheory/FreeGroup/Basic.lean b/Mathlib/GroupTheory/FreeGroup/Basic.lean index 3a4a00e0b12b57..4fdc5d6b3b1b88 100644 --- a/Mathlib/GroupTheory/FreeGroup/Basic.lean +++ b/Mathlib/GroupTheory/FreeGroup/Basic.lean @@ -203,7 +203,7 @@ theorem church_rosser : Red L₁ L₂ → Red L₁ L₃ → Join Red L₂ L₃ : @[to_additive] theorem cons_cons {p} : Red L₁ L₂ → Red (p :: L₁) (p :: L₂) := - ReflTransGen.lift (List.cons p) fun _ _ => Step.cons + ReflTransGen.lift (List.cons p) (fun _ _ => Step.cons) L₁ L₂ @[to_additive] theorem cons_cons_iff (p) : Red (p :: L₁) (p :: L₂) ↔ Red L₁ L₂ := @@ -322,25 +322,20 @@ theorem Step.sublist (H : Red.Step L₁ L₂) : L₂ <+ L₁ := by @[to_additive /-- If `w₁ w₂` are words such that `w₁` reduces to `w₂`, then `w₂` is a sublist of `w₁`. -/] protected theorem sublist : Red L₁ L₂ → L₂ <+ L₁ := - @reflTransGen_of_isTrans_reflexive - _ (fun a b => b <+ a) _ _ _ - ⟨List.Sublist.refl⟩ - ⟨fun _a _b _c hab hbc => List.Sublist.trans hbc hab⟩ - (fun _ _ => Red.Step.sublist) + @reflTransGen_le_of_le _ (fun a b => b <+ a) _ ⟨List.Sublist.refl⟩ + ⟨fun _a _b _c hab hbc => List.Sublist.trans hbc hab⟩ (fun _ _ => Red.Step.sublist) L₁ L₂ @[to_additive] theorem length_le (h : Red L₁ L₂) : L₂.length ≤ L₁.length := h.sublist.length_le -set_option linter.auxLemma false in @[to_additive (attr := deprecated "Should not be needed." (since := "2026-04-10"))] theorem sizeof_of_step : ∀ {L₁ L₂ : List (α × Bool)}, Step L₁ L₂ → sizeOf L₂ < sizeOf L₁ | _, _, @Step.not _ L1 L2 x b => by induction L1 with | nil => - -- This was just `dsimp` prior to https://github.com/leanprover/lean4/pull/13320 - dsimp [sizeOf, _sizeOf_1] + rw [nil_append, nil_append, cons.sizeOf_spec, cons.sizeOf_spec] lia | cons hd tl ih => dsimp @@ -371,7 +366,7 @@ theorem equivalence_join_red : Equivalence (Join (@Red α)) := @[to_additive] theorem join_red_of_step (h : Red.Step L₁ L₂) : Join Red L₁ L₂ := by unfold Red - exact join_of_single h.to_red + exact le_join_of_refl L₁ L₂ h.to_red @[to_additive] theorem eqvGen_step_iff_join_red : EqvGen Red.Step L₁ L₂ ↔ Join Red L₁ L₂ := @@ -379,8 +374,8 @@ theorem eqvGen_step_iff_join_red : EqvGen Red.Step L₁ L₂ ↔ Join Red L₁ L (fun h => have : EqvGen (Join Red) L₁ L₂ := h.mono fun _ _ => join_red_of_step equivalence_join_red.eqvGen_iff.1 this) - (join_of_equivalence (Relation.EqvGen.is_equivalence _) fun _ _ => - reflTransGen_of_equivalence (Relation.EqvGen.is_equivalence _) EqvGen.rel) + (join_le_of_equivalence_of_le (Relation.EqvGen.is_equivalence _) + (reflTransGen_le_of_equivalence_of_le (Relation.EqvGen.is_equivalence _) EqvGen.rel) L₁ L₂) /-! ### Reduced words -/ @@ -591,7 +586,7 @@ theorem Red.Step.invRev {L₁ L₂ : List (α × Bool)} (h : Red.Step L₁ L₂) @[to_additive] theorem Red.invRev {L₁ L₂ : List (α × Bool)} (h : Red L₁ L₂) : Red (invRev L₁) (invRev L₂) := - Relation.ReflTransGen.lift _ (fun _a _b => Red.Step.invRev) h + Relation.ReflTransGen.lift FreeGroup.invRev (fun _a _b => Red.Step.invRev) L₁ L₂ h @[to_additive (attr := simp)] theorem Red.step_invRev_iff : diff --git a/Mathlib/GroupTheory/GroupAction/Basic.lean b/Mathlib/GroupTheory/GroupAction/Basic.lean index 4c8a3f07e6cf7b..73dfdc011f2e33 100644 --- a/Mathlib/GroupTheory/GroupAction/Basic.lean +++ b/Mathlib/GroupTheory/GroupAction/Basic.lean @@ -359,3 +359,11 @@ lemma Module.stabilizer_units_eq_bot_of_ne_zero {x : M} (hx : x ≠ 0) : rw [← sub_eq_zero, ← smul_eq_zero_iff_left hx, Units.val_one, sub_smul, hg, one_smul, sub_self] end + +@[simp] lemma Multiplicative.mulAction_orbit {α β : Type*} [VAdd α β] (b : β) : + MulAction.orbit (Multiplicative α) b = AddAction.orbit α b := + rfl + +@[simp] lemma Additive.mulAction_orbit {α β : Type*} [SMul α β] (b : β) : + AddAction.orbit (Additive α) b = MulAction.orbit α b := + rfl diff --git a/Mathlib/GroupTheory/GroupAction/Hom.lean b/Mathlib/GroupTheory/GroupAction/Hom.lean index c1906b4166e9c1..4ba57e5936c038 100644 --- a/Mathlib/GroupTheory/GroupAction/Hom.lean +++ b/Mathlib/GroupTheory/GroupAction/Hom.lean @@ -448,7 +448,6 @@ lemma coe_add [SMul M X] [AddZeroClass Y] [DistribSMul N Y] (f g : X →ₑ[σ] instance [SMul M X] [AddMonoid Y] [DistribSMul N Y] : AddMonoid (X →ₑ[σ] Y) where add_assoc _ _ _ := ext fun _ ↦ add_assoc _ _ _ - nsmul n f := n • f nsmul_zero f := ext fun x ↦ AddMonoid.nsmul_zero (f x) nsmul_succ n f := ext fun x ↦ AddMonoid.nsmul_succ n (f x) @@ -484,7 +483,6 @@ instance [SMul M X] [AddGroup Y] [DistribSMul N Y] : AddGroup (X →ₑ[σ] Y) w neg f := ⟨-f, by simp⟩ neg_add_cancel f := ext fun _ ↦ neg_add_cancel _ sub_eq_add_neg _ _ := ext fun _ ↦ sub_eq_add_neg _ _ - zsmul z f := z • f zsmul_zero' f := ext fun x ↦ SubNegMonoid.zsmul_zero' _ zsmul_neg' _ _ := ext fun x ↦ SubNegMonoid.zsmul_neg' _ _ zsmul_succ' _ _ := ext fun x ↦ SubNegMonoid.zsmul_succ' _ _ diff --git a/Mathlib/GroupTheory/GroupAction/Quotient.lean b/Mathlib/GroupTheory/GroupAction/Quotient.lean index e5ac2d4b6cbbaa..5ccc2b03d56143 100644 --- a/Mathlib/GroupTheory/GroupAction/Quotient.lean +++ b/Mathlib/GroupTheory/GroupAction/Quotient.lean @@ -32,7 +32,7 @@ assert_not_exists Cardinal universe u v w -variable {α : Type u} {β : Type v} {γ : Type w} +variable {G : Type u} {X : Type v} open Function @@ -40,75 +40,75 @@ open scoped commutatorElement namespace MulAction -variable [Group α] +variable [Group G] section QuotientAction open Subgroup MulOpposite QuotientGroup -variable (β) [Monoid β] [MulAction β α] (H : Subgroup α) +variable (X) [Monoid X] [MulAction X G] (H : Subgroup G) -/-- A typeclass for when a `MulAction β α` descends to the quotient `α ⧸ H`. -/ +/-- A typeclass for when a `MulAction X G` descends to the quotient `G ⧸ H`. -/ class QuotientAction : Prop where /-- The action fulfils a normality condition on products that lie in `H`. - This ensures that the action descends to an action on the quotient `α ⧸ H`. -/ - inv_mul_mem : ∀ (b : β) {a a' : α}, a⁻¹ * a' ∈ H → (b • a)⁻¹ * b • a' ∈ H + This ensures that the action descends to an action on the quotient `G ⧸ H`. -/ + inv_mul_mem : ∀ (b : X) {a a' : G}, a⁻¹ * a' ∈ H → (b • a)⁻¹ * b • a' ∈ H -/-- A typeclass for when an `AddAction β α` descends to the quotient `α ⧸ H`. -/ -class _root_.AddAction.QuotientAction {α : Type u} (β : Type v) [AddGroup α] [AddMonoid β] - [AddAction β α] (H : AddSubgroup α) : Prop where +/-- A typeclass for when an `AddAction X G` descends to the quotient `G ⧸ H`. -/ +class _root_.AddAction.QuotientAction {G : Type u} (X : Type v) [AddGroup G] [AddMonoid X] + [AddAction X G] (H : AddSubgroup G) : Prop where /-- The action fulfils a normality condition on summands that lie in `H`. - This ensures that the action descends to an action on the quotient `α ⧸ H`. -/ - inv_mul_mem : ∀ (b : β) {a a' : α}, -a + a' ∈ H → -(b +ᵥ a) + (b +ᵥ a') ∈ H + This ensures that the action descends to an action on the quotient `G ⧸ H`. -/ + inv_mul_mem : ∀ (x : X) {g g' : G}, -g + g' ∈ H → -(x +ᵥ g) + (x +ᵥ g') ∈ H attribute [to_additive] MulAction.QuotientAction @[to_additive] -instance left_quotientAction : QuotientAction α H := +instance left_quotientAction : QuotientAction G H := ⟨fun _ _ _ _ => by rwa [smul_eq_mul, smul_eq_mul, mul_inv_rev, mul_assoc, inv_mul_cancel_left]⟩ @[to_additive] -instance right_quotientAction : QuotientAction (normalizer H : Subgroup α).op H := +instance right_quotientAction : QuotientAction (normalizer H : Subgroup G).op H := ⟨fun b c _ _ => by rwa [smul_def, smul_def, smul_eq_mul_unop, smul_eq_mul_unop, mul_inv_rev, ← mul_assoc, mem_normalizer_iff'.mp b.prop, mul_assoc, mul_inv_cancel_left]⟩ @[to_additive] -instance right_quotientAction' [hH : H.Normal] : QuotientAction αᵐᵒᵖ H := +instance right_quotientAction' [hH : H.Normal] : QuotientAction Gᵐᵒᵖ H := ⟨fun _ _ _ _ => by rwa [smul_eq_mul_unop, smul_eq_mul_unop, mul_inv_rev, mul_assoc, hH.mem_comm_iff, mul_assoc, mul_inv_cancel_right]⟩ @[to_additive] -instance quotient [QuotientAction β H] : MulAction β (α ⧸ H) where +instance quotient [QuotientAction X H] : MulAction X (G ⧸ H) where smul b := Quotient.map' (b • ·) fun _ _ h => leftRel_apply.mpr <| QuotientAction.inv_mul_mem b <| leftRel_apply.mp h - one_smul q := Quotient.inductionOn' q fun a => congr_arg Quotient.mk'' (one_smul β a) + one_smul q := Quotient.inductionOn' q fun a => congr_arg Quotient.mk'' (one_smul X a) mul_smul b b' q := Quotient.inductionOn' q fun a => congr_arg Quotient.mk'' (mul_smul b b' a) -variable {β} +variable {X} @[to_additive (attr := simp)] -theorem Quotient.smul_mk [QuotientAction β H] (b : β) (a : α) : - (b • QuotientGroup.mk a : α ⧸ H) = QuotientGroup.mk (b • a) := +theorem Quotient.smul_mk [QuotientAction X H] (b : X) (g : G) : + (b • QuotientGroup.mk g : G ⧸ H) = QuotientGroup.mk (b • g) := rfl @[to_additive (attr := simp)] -theorem Quotient.smul_coe [QuotientAction β H] (b : β) (a : α) : - b • (a : α ⧸ H) = (↑(b • a) : α ⧸ H) := +theorem Quotient.smul_coe [QuotientAction X H] (b : X) (g : G) : + b • (g : G ⧸ H) = (↑(b • g) : G ⧸ H) := rfl @[to_additive (attr := simp)] -theorem Quotient.mk_smul_out [QuotientAction β H] (b : β) (q : α ⧸ H) : +theorem Quotient.mk_smul_out [QuotientAction X H] (b : X) (q : G ⧸ H) : QuotientGroup.mk (b • q.out) = b • q := by rw [← Quotient.smul_mk, QuotientGroup.out_eq'] @[to_additive] -theorem Quotient.coe_smul_out [QuotientAction β H] (b : β) (q : α ⧸ H) : ↑(b • q.out) = b • q := by +theorem Quotient.coe_smul_out [QuotientAction X H] (b : X) (q : G ⧸ H) : ↑(b • q.out) = b • q := by simp -theorem _root_.QuotientGroup.out_conj_pow_minimalPeriod_mem (a : α) (q : α ⧸ H) : - q.out⁻¹ * a ^ Function.minimalPeriod (a • ·) q * q.out ∈ H := by +theorem _root_.QuotientGroup.out_conj_pow_minimalPeriod_mem (g : G) (q : G ⧸ H) : + q.out⁻¹ * g ^ Function.minimalPeriod (g • ·) q * q.out ∈ H := by rw [mul_assoc, ← QuotientGroup.eq, QuotientGroup.out_eq', ← smul_eq_mul, Quotient.mk_smul_out, eq_comm, pow_smul_eq_iff_minimalPeriod_dvd] @@ -117,51 +117,51 @@ end QuotientAction open QuotientGroup /-- The canonical map to the left cosets. -/ -def _root_.MulActionHom.toQuotient (H : Subgroup α) : α →[α] α ⧸ H where +def _root_.MulActionHom.toQuotient (H : Subgroup G) : G →[G] G ⧸ H where toFun := (↑); map_smul' := Quotient.smul_coe H @[simp] -theorem _root_.MulActionHom.toQuotient_apply (H : Subgroup α) (g : α) : +theorem _root_.MulActionHom.toQuotient_apply (H : Subgroup G) (g : G) : MulActionHom.toQuotient H g = g := rfl @[to_additive (attr := simp)] -theorem coe_quotient_smul {H : Subgroup α} [H.Normal] [SMul α β] - [MulAction (α ⧸ H) β] [IsScalarTower α (α ⧸ H) β] (g : α) (x : β) : - (g : α ⧸ H) • x = g • x := by - rw [← smul_one_smul (α ⧸ H) g x, ← QuotientGroup.mk_one, Quotient.smul_coe, +theorem coe_quotient_smul {H : Subgroup G} [H.Normal] [SMul G X] + [MulAction (G ⧸ H) X] [IsScalarTower G (G ⧸ H) X] (g : G) (x : X) : + (g : G ⧸ H) • x = g • x := by + rw [← smul_one_smul (G ⧸ H) g x, ← QuotientGroup.mk_one, Quotient.smul_coe, smul_eq_mul, mul_one] @[to_additive] -instance mulLeftCosetsCompSubtypeVal (H I : Subgroup α) : MulAction I (α ⧸ H) := - MulAction.compHom (α ⧸ H) (Subgroup.subtype I) +instance mulLeftCosetsCompSubtypeVal (H I : Subgroup G) : MulAction I (G ⧸ H) := + MulAction.compHom (G ⧸ H) (Subgroup.subtype I) -variable (α) -variable [MulAction α β] (x : β) +variable (G) +variable [MulAction G X] (x : X) /-- The canonical map from the quotient of the stabilizer to the set. -/ @[to_additive /-- The canonical map from the quotient of the stabilizer to the set. -/] -def ofQuotientStabilizer (g : α ⧸ MulAction.stabilizer α x) : β := +def ofQuotientStabilizer (g : G ⧸ MulAction.stabilizer G x) : X := Quotient.liftOn' g (· • x) fun g1 g2 H => calc g1 • x = g1 • (g1⁻¹ * g2) • x := congr_arg _ (leftRel_apply.mp H).symm _ = g2 • x := by rw [smul_smul, mul_inv_cancel_left] @[to_additive (attr := simp)] -theorem ofQuotientStabilizer_mk (g : α) : ofQuotientStabilizer α x (QuotientGroup.mk g) = g • x := +theorem ofQuotientStabilizer_mk (g : G) : ofQuotientStabilizer G x (QuotientGroup.mk g) = g • x := rfl @[to_additive] -theorem ofQuotientStabilizer_mem_orbit (g) : ofQuotientStabilizer α x g ∈ orbit α x := +theorem ofQuotientStabilizer_mem_orbit (g) : ofQuotientStabilizer G x g ∈ orbit G x := Quotient.inductionOn' g fun g => ⟨g, rfl⟩ @[to_additive] -theorem ofQuotientStabilizer_smul (g : α) (g' : α ⧸ MulAction.stabilizer α x) : - ofQuotientStabilizer α x (g • g') = g • ofQuotientStabilizer α x g' := +theorem ofQuotientStabilizer_smul (g : G) (g' : G ⧸ MulAction.stabilizer G x) : + ofQuotientStabilizer G x (g • g') = g • ofQuotientStabilizer G x g' := Quotient.inductionOn' g' fun _ => mul_smul _ _ _ @[to_additive] -theorem injective_ofQuotientStabilizer : Function.Injective (ofQuotientStabilizer α x) := +theorem injective_ofQuotientStabilizer : Function.Injective (ofQuotientStabilizer G x) := fun y₁ y₂ => Quotient.inductionOn₂' y₁ y₂ fun g₁ g₂ (H : g₁ • x = g₂ • x) => Quotient.sound' <| by @@ -171,29 +171,29 @@ theorem injective_ofQuotientStabilizer : Function.Injective (ofQuotientStabilize /-- **Orbit-stabilizer theorem**. -/ @[to_additive /-- Orbit-stabilizer theorem. -/] -noncomputable def orbitEquivQuotientStabilizer (b : β) : orbit α b ≃ α ⧸ stabilizer α b := +noncomputable def orbitEquivQuotientStabilizer (b : X) : orbit G b ≃ G ⧸ stabilizer G b := Equiv.symm <| - Equiv.ofBijective (fun g => ⟨ofQuotientStabilizer α b g, ofQuotientStabilizer_mem_orbit α b g⟩) - ⟨fun x y hxy => injective_ofQuotientStabilizer α b (by convert! congr_arg Subtype.val hxy), + Equiv.ofBijective (fun g => ⟨ofQuotientStabilizer G b g, ofQuotientStabilizer_mem_orbit G b g⟩) + ⟨fun x y hxy => injective_ofQuotientStabilizer G b (by convert! congr_arg Subtype.val hxy), fun ⟨_, ⟨g, hgb⟩⟩ => ⟨g, Subtype.ext hgb⟩⟩ /-- Orbit-stabilizer theorem. -/ @[to_additive AddAction.orbitProdStabilizerEquivAddGroup /-- Orbit-stabilizer theorem. -/] -noncomputable def orbitProdStabilizerEquivGroup (b : β) : orbit α b × stabilizer α b ≃ α := - (Equiv.prodCongr (orbitEquivQuotientStabilizer α _) (Equiv.refl _)).trans +noncomputable def orbitProdStabilizerEquivGroup (b : X) : orbit G b × stabilizer G b ≃ G := + (Equiv.prodCongr (orbitEquivQuotientStabilizer G _) (Equiv.refl _)).trans Subgroup.groupEquivQuotientProdSubgroup.symm /-- Orbit-stabilizer theorem. -/ @[to_additive AddAction.card_orbit_mul_card_stabilizer_eq_card_addGroup /-- Orbit-stabilizer theorem. -/] -theorem card_orbit_mul_card_stabilizer_eq_card_group (b : β) [Fintype α] [Fintype <| orbit α b] - [Fintype <| stabilizer α b] : - Fintype.card (orbit α b) * Fintype.card (stabilizer α b) = Fintype.card α := by - rw [← Fintype.card_prod, Fintype.card_congr (orbitProdStabilizerEquivGroup α b)] +theorem card_orbit_mul_card_stabilizer_eq_card_group (b : X) [Fintype G] [Fintype <| orbit G b] + [Fintype <| stabilizer G b] : + Fintype.card (orbit G b) * Fintype.card (stabilizer G b) = Fintype.card G := by + rw [← Fintype.card_prod, Fintype.card_congr (orbitProdStabilizerEquivGroup G b)] @[to_additive (attr := simp)] -theorem orbitEquivQuotientStabilizer_symm_apply (b : β) (a : α) : - ((orbitEquivQuotientStabilizer α b).symm a : β) = a • b := +theorem orbitEquivQuotientStabilizer_symm_apply (b : X) (g : G) : + ((orbitEquivQuotientStabilizer G b).symm g : X) = g • b := rfl @[to_additive (attr := simp)] @@ -202,75 +202,77 @@ theorem stabilizer_quotient {G} [Group G] (H : Subgroup G) : ext simp [QuotientGroup.eq] -variable (β) +variable (X) -local notation "Ω" => Quotient <| orbitRel α β +local notation "Ω" => Quotient <| orbitRel G X -/-- **Class formula** : given `G` a group acting on `X` and `φ` a function mapping each orbit of `X` -under this action (that is, each element of the quotient of `X` by the relation `orbitRel G X`) to -an element in this orbit, this gives a (noncomputable) bijection between `X` and the disjoint union -of `G/Stab(φ(ω))` over all orbits `ω`. In most cases you'll want `φ` to be `Quotient.out`, so we -provide `MulAction.selfEquivSigmaOrbitsQuotientStabilizer'` as a special case. -/ +/-- **Class formula** : let `G` be a group acting on `X` and let `φ` be a function mapping each +orbit of `X` under this action (that is, each element of the quotient of `G` by the relation +`orbitRel G X`) to an element in this orbit. We provide a (noncomputable) bijection between `X` +and the disjoint union of `G/Stab(φ(ω))` over all orbits `ω : Ω`. In most cases you'll want `φ` +to be `Quotient.out`, so we provide `MulAction.selfEquivSigmaOrbitsQuotientStabilizer'` as a +special case. -/ @[to_additive - /-- **Class formula** : given `G` an additive group acting on `X` and `φ` a function - mapping each orbit of `X` under this action (that is, each element of the quotient of `X` by - the relation `orbit_rel G X`) to an element in this orbit, this gives a (noncomputable) - bijection between `X` and the disjoint union of `G/Stab(φ(ω))` over all orbits `ω`. In most - cases you'll want `φ` to be `Quotient.out`, so we provide + /-- **Class formula** : let `G` be an additive group acting on `X` and let `φ` be a function + mapping each orbit of `X` under this action (that is, each element of the quotient of `X` by + the relation `orbitRel G X`) to an element in this orbit. This definition is a (noncomputable) + bijection between `X` and the disjoint union of `G/Stab(φ(ω))` over all orbits `ω : Ω`. In + most cases you'll want `φ` to be `Quotient.out`, so we provide `AddAction.selfEquivSigmaOrbitsQuotientStabilizer'` as a special case. -/] -noncomputable def selfEquivSigmaOrbitsQuotientStabilizer' {φ : Ω → β} - (hφ : LeftInverse Quotient.mk'' φ) : β ≃ Σ ω : Ω, α ⧸ stabilizer α (φ ω) := +noncomputable def selfEquivSigmaOrbitsQuotientStabilizer' {φ : Ω → X} + (hφ : LeftInverse Quotient.mk'' φ) : X ≃ Σ ω : Ω, G ⧸ stabilizer G (φ ω) := calc - β ≃ Σ ω : Ω, orbitRel.Quotient.orbit ω := selfEquivSigmaOrbits' α β - _ ≃ Σ ω : Ω, α ⧸ stabilizer α (φ ω) := + X ≃ Σ ω : Ω, orbitRel.Quotient.orbit ω := selfEquivSigmaOrbits' G X + _ ≃ Σ ω : Ω, G ⧸ stabilizer G (φ ω) := Equiv.sigmaCongrRight fun ω => (Equiv.setCongr <| orbitRel.Quotient.orbit_eq_orbit_out _ hφ).trans <| - orbitEquivQuotientStabilizer α (φ ω) + orbitEquivQuotientStabilizer G (φ ω) /-- **Class formula**. This is a special case of `MulAction.self_equiv_sigma_orbits_quotient_stabilizer'` with `φ = Quotient.out`. -/ @[to_additive /-- **Class formula**. This is a special case of `AddAction.self_equiv_sigma_orbits_quotient_stabilizer'` with `φ = Quotient.out`. -/] -noncomputable def selfEquivSigmaOrbitsQuotientStabilizer : β ≃ Σ ω : Ω, α ⧸ stabilizer α ω.out := - selfEquivSigmaOrbitsQuotientStabilizer' α β Quotient.out_eq' +noncomputable def selfEquivSigmaOrbitsQuotientStabilizer : X ≃ Σ ω : Ω, G ⧸ stabilizer G ω.out := + selfEquivSigmaOrbitsQuotientStabilizer' G X Quotient.out_eq' /-- **Burnside's lemma** : a (noncomputable) bijection between the disjoint union of all -`{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × X/G`, where `G` is a group acting on `X` and -`X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/ +`{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × Ω`, where `G` is a group acting on `X` +and `Ω = X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/ @[to_additive AddAction.sigmaFixedByEquivOrbitsProdAddGroup /-- **Burnside's lemma** : a (noncomputable) bijection between the disjoint union of all - `{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × X/G`, where `G` is an additive group - acting on `X` and `X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/] -noncomputable def sigmaFixedByEquivOrbitsProdGroup : (Σ a : α, fixedBy β a) ≃ Ω × α := + `{x ∈ X | g • x = x}` for `g ∈ G` and the product `G × Ω`, where `G` is an additive group + acting on `X` and `Ω = X/G` denotes the quotient of `X` by the relation `orbitRel G X`. -/] +noncomputable def sigmaFixedByEquivOrbitsProdGroup : (Σ g : G, fixedBy X g) ≃ Ω × G := calc - (Σ a : α, fixedBy β a) ≃ { ab : α × β // ab.1 • ab.2 = ab.2 } := + (Σ g : G, fixedBy X g) ≃ { ab : G × X // ab.1 • ab.2 = ab.2 } := (Equiv.subtypeProdEquivSigmaSubtype _).symm - _ ≃ { ba : β × α // ba.2 • ba.1 = ba.1 } := (Equiv.prodComm α β).subtypeEquiv fun _ => Iff.rfl - _ ≃ Σ b : β, stabilizer α b := - Equiv.subtypeProdEquivSigmaSubtype fun (b : β) a => a ∈ stabilizer α b - _ ≃ Σ ωb : Σ ω : Ω, orbit α ω.out, stabilizer α (ωb.2 : β) := - (selfEquivSigmaOrbits α β).sigmaCongrLeft' - _ ≃ Σ ω : Ω, Σ b : orbit α ω.out, stabilizer α (b : β) := - Equiv.sigmaAssoc fun (ω : Ω) (b : orbit α ω.out) => stabilizer α (b : β) - _ ≃ Σ ω : Ω, Σ _ : orbit α ω.out, stabilizer α ω.out := + _ ≃ { ba : X × G // ba.2 • ba.1 = ba.1 } := (Equiv.prodComm G X).subtypeEquiv fun _ => Iff.rfl + _ ≃ Σ b : X, stabilizer G b := + Equiv.subtypeProdEquivSigmaSubtype fun (b : X) a => a ∈ stabilizer G b + _ ≃ Σ ωb : Σ ω : Ω, orbit G ω.out, stabilizer G (ωb.2 : X) := + (selfEquivSigmaOrbits G X).sigmaCongrLeft' + _ ≃ Σ ω : Ω, Σ b : orbit G ω.out, stabilizer G (b : X) := + Equiv.sigmaAssoc fun (ω : Ω) (b : orbit G ω.out) => stabilizer G (b : X) + _ ≃ Σ ω : Ω, Σ _ : orbit G ω.out, stabilizer G ω.out := Equiv.sigmaCongrRight fun _ => Equiv.sigmaCongrRight fun ⟨_, hb⟩ => (stabilizerEquivStabilizerOfOrbitRel hb).toEquiv - _ ≃ Σ ω : Ω, orbit α ω.out × stabilizer α ω.out := + _ ≃ Σ ω : Ω, orbit G ω.out × stabilizer G ω.out := Equiv.sigmaCongrRight fun _ => Equiv.sigmaEquivProd _ _ - _ ≃ Σ _ : Ω, α := Equiv.sigmaCongrRight fun ω => orbitProdStabilizerEquivGroup α ω.out - _ ≃ Ω × α := Equiv.sigmaEquivProd Ω α + _ ≃ Σ _ : Ω, G := Equiv.sigmaCongrRight fun ω => orbitProdStabilizerEquivGroup G ω.out + _ ≃ Ω × G := Equiv.sigmaEquivProd Ω G -/-- **Burnside's lemma** : given a finite group `G` acting on a set `X`, the average number of -elements fixed by each `g ∈ G` is the number of orbits. -/ +/-- **Burnside's lemma** : given a finite group `G` acting on a type `X`, the sum the orders of the +stabilisers coincides with the number of orbits multiplied by the order of `G`. -/ @[to_additive (attr := wikidata Q1330377) AddAction.sum_card_fixedBy_eq_card_orbits_mul_card_addGroup - /-- **Burnside's lemma** : given a finite additive group `G` acting on a set `X`, - the average number of elements fixed by each `g ∈ G` is the number of orbits. -/] -theorem sum_card_fixedBy_eq_card_orbits_mul_card_group [Fintype α] [∀ a : α, Fintype <| fixedBy β a] - [Fintype Ω] : (∑ a : α, Fintype.card (fixedBy β a)) = Fintype.card Ω * Fintype.card α := by + /-- **Burnside's lemma** : given a finite additive group `G` acting on a type `X`, + the sum the orders of the stabilisers coincides with the number of orbits multiplied by the + order of `G`. -/] +theorem sum_card_fixedBy_eq_card_orbits_mul_card_group [Fintype G] [∀ g : G, Fintype <| fixedBy X g] + [Fintype Ω] : (∑ g : G, Fintype.card (fixedBy X g)) = Fintype.card Ω * Fintype.card G := by rw [← Fintype.card_prod, ← Fintype.card_sigma, - Fintype.card_congr (sigmaFixedByEquivOrbitsProdGroup α β)] + Fintype.card_congr (sigmaFixedByEquivOrbitsProdGroup G X)] @[to_additive] instance isPretransitive_quotient (G) [Group G] (H : Subgroup G) : IsPretransitive G (G ⧸ H) where @@ -279,16 +281,16 @@ instance isPretransitive_quotient (G) [Group G] (H : Subgroup G) : IsPretransiti refine ⟨y * x⁻¹, QuotientGroup.eq.mpr ?_⟩ simp only [smul_eq_mul, H.one_mem, inv_mul_cancel, inv_mul_cancel_right]} -variable {α} +variable {G} @[to_additive] -instance finite_quotient_of_pretransitive_of_finite_quotient [IsPretransitive α β] {H : Subgroup α} - [Finite (α ⧸ H)] : Finite <| orbitRel.Quotient H β := by - rcases isEmpty_or_nonempty β with he | ⟨⟨b⟩⟩ +instance finite_quotient_of_pretransitive_of_finite_quotient [IsPretransitive G X] {H : Subgroup G} + [Finite (G ⧸ H)] : Finite <| orbitRel.Quotient H X := by + rcases isEmpty_or_nonempty X with he | ⟨⟨b⟩⟩ · exact Quotient.finite _ · have h' : Finite (Quotient (rightRel H)) := Finite.of_equiv _ (quotientRightRelEquivQuotientLeftRel _).symm - let f : Quotient (rightRel H) → orbitRel.Quotient H β := + let f : Quotient (rightRel H) → orbitRel.Quotient H X := fun a ↦ Quotient.liftOn' a (fun g ↦ ⟦g • b⟧) fun g₁ g₂ r ↦ by replace r := Setoid.symm' _ r rw [rightRel_eq] at r @@ -297,14 +299,14 @@ instance finite_quotient_of_pretransitive_of_finite_quotient [IsPretransitive α exact Finite.of_surjective f ((Quotient.surjective_liftOn' _).2 (Quotient.mk''_surjective.comp (MulAction.surjective_smul _ _))) -variable {β} in +variable {X} in /-- A bijection between the quotient of the action of a subgroup `H` on an orbit, and a corresponding quotient expressed in terms of `Setoid.comap Subtype.val`. -/ @[to_additive /-- A bijection between the quotient of the action of an additive subgroup `H` on an orbit, and a corresponding quotient expressed in terms of `Setoid.comap Subtype.val`. -/] -noncomputable def equivSubgroupOrbitsSetoidComap (H : Subgroup α) (ω : Ω) : +noncomputable def equivSubgroupOrbitsSetoidComap (H : Subgroup G) (ω : Ω) : orbitRel.Quotient H (orbitRel.Quotient.orbit ω) ≃ - Quotient ((orbitRel H β).comap (Subtype.val : Quotient.mk (orbitRel α β) ⁻¹' {ω} → β)) where + Quotient ((orbitRel H X).comap (Subtype.val : Quotient.mk (orbitRel G X) ⁻¹' {ω} → X)) where toFun := fun q ↦ q.liftOn' (fun x ↦ ⟦⟨↑x, by simp only [Set.mem_preimage, Set.mem_singleton_iff] have hx := x.property @@ -330,43 +332,43 @@ noncomputable def equivSubgroupOrbitsSetoidComap (H : Subgroup α) (ω : Ω) : induction q using Quotient.inductionOn' rfl -/-- A bijection between the orbits under the action of a subgroup `H` on `β`, and the orbits +/-- A bijection between the orbits under the action of a subgroup `H` on `X`, and the orbits under the action of `H` on each orbit under the action of `G`. -/ @[to_additive /-- A bijection between the orbits under the action of an additive subgroup `H` on -`β`, and the orbits under the action of `H` on each orbit under the action of `G`. -/] -noncomputable def equivSubgroupOrbits (H : Subgroup α) : - orbitRel.Quotient H β ≃ Σ ω : Ω, orbitRel.Quotient H (orbitRel.Quotient.orbit ω) := +`X`, and the orbits under the action of `H` on each orbit under the action of `G`. -/] +noncomputable def equivSubgroupOrbits (H : Subgroup G) : + orbitRel.Quotient H X ≃ Σ ω : Ω, orbitRel.Quotient H (orbitRel.Quotient.orbit ω) := (Setoid.sigmaQuotientEquivOfLe (orbitRel_subgroup_le H)).symm.trans (Equiv.sigmaCongrRight fun ω ↦ (equivSubgroupOrbitsSetoidComap H ω).symm) -variable {β} +variable {X} @[to_additive] -instance finite_quotient_of_finite_quotient_of_finite_quotient {H : Subgroup α} - [Finite (orbitRel.Quotient α β)] [Finite (α ⧸ H)] : - Finite <| orbitRel.Quotient H β := by - rw [(equivSubgroupOrbits β H).finite_iff] +instance finite_quotient_of_finite_quotient_of_finite_quotient {H : Subgroup G} + [Finite (orbitRel.Quotient G X)] [Finite (G ⧸ H)] : + Finite <| orbitRel.Quotient H X := by + rw [(equivSubgroupOrbits X H).finite_iff] infer_instance /-- Given a group acting freely and transitively, an equivalence between the orbits under the -action of a subgroup and the quotient group. -/ +action of a subgroup and the quotient of the group by the subgroup. -/ @[to_additive /-- Given an additive group acting freely and transitively, an equivalence between the -orbits under the action of an additive subgroup and the quotient group. -/] -noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive α β] - [IsCancelSMul α β] (H : Subgroup α) : - orbitRel.Quotient H β ≃ α ⧸ H where - toFun := fun q ↦ q.liftOn' (fun y ↦ (exists_smul_eq α y x).choose) (by +orbits under the action of an additive subgroup and the quotient of the group by the subgroup. -/] +noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive G X] + [IsCancelSMul G X] (H : Subgroup G) : + orbitRel.Quotient H X ≃ G ⧸ H where + toFun := fun q ↦ q.liftOn' (fun y ↦ (exists_smul_eq G y x).choose) (by intro y₁ y₂ h rw [orbitRel_apply] at h rw [Quotient.eq'', leftRel_eq] dsimp only rcases h with ⟨g, rfl⟩ dsimp only - suffices (exists_smul_eq α (g • y₂) x).choose = (exists_smul_eq α y₂ x).choose * g⁻¹ by + suffices (exists_smul_eq G (g • y₂) x).choose = (exists_smul_eq G y₂ x).choose * g⁻¹ by simp [this] refine IsCancelSMul.right_cancel _ _ (g • y₂) ?_ - rw [(exists_smul_eq α (g • y₂) x).choose_spec, Subgroup.smul_def, Subgroup.coe_inv, - smul_smul, inv_mul_cancel_right, (exists_smul_eq α y₂ x).choose_spec]) + rw [(exists_smul_eq G (g • y₂) x).choose_spec, Subgroup.smul_def, Subgroup.coe_inv, + smul_smul, inv_mul_cancel_right, (exists_smul_eq G y₂ x).choose_spec]) invFun := fun q ↦ q.liftOn' (fun g ↦ ⟦g⁻¹ • x⟧) (by intro g₁ g₂ h rw [leftRel_eq] at h @@ -377,7 +379,7 @@ noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive α β] 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 α _ x).choose_spec] + rw [inv_smul_eq_iff, (exists_smul_eq G _ x).choose_spec] right_inv := fun g ↦ by cases g using Quotient.inductionOn' with | _ g simp only [Quotient.liftOn'_mk'', QuotientGroup.mk] @@ -386,30 +388,30 @@ noncomputable def equivSubgroupOrbitsQuotientGroup [IsPretransitive α β] convert! one_mem H rw [inv_mul_eq_one, eq_comm, ← inv_mul_eq_one, ← Subgroup.mem_bot, ← IsCancelSMul.stabilizer_eq_bot (g⁻¹ • x), mem_stabilizer_iff, mul_smul, - (exists_smul_eq α (g⁻¹ • x) x).choose_spec] + (exists_smul_eq G (g⁻¹ • x) x).choose_spec] -/-- If `α` acts on `β` with trivial stabilizers, `β` is equivalent -to the product of the quotient of `β` by `α` and `α`. +/-- If `G` acts on `X` with trivial stabilizers, `X` is equivalent +to the product of the quotient of `X` by `G` and `G`. See `MulAction.selfEquivOrbitsQuotientProd` with `φ = Quotient.out`. -/ -@[to_additive selfEquivOrbitsQuotientProd' /-- If `α` acts freely on `β`, `β` is equivalent -to the product of the quotient of `β` by `α` and `α`. +@[to_additive selfEquivOrbitsQuotientProd' /-- If `G` acts freely on `X`, `X` is equivalent +to the product of the quotient of `X` by `G` and `G`. See `AddAction.selfEquivOrbitsQuotientProd` with `φ = Quotient.out`. -/] noncomputable def selfEquivOrbitsQuotientProd' - {φ : Quotient (MulAction.orbitRel α β) → β} (hφ : Function.LeftInverse Quotient.mk'' φ) - (h : ∀ b : β, MulAction.stabilizer α b = ⊥) : - β ≃ Quotient (MulAction.orbitRel α β) × α := - (MulAction.selfEquivSigmaOrbitsQuotientStabilizer' α β hφ).trans <| + {φ : Quotient (MulAction.orbitRel G X) → X} (hφ : Function.LeftInverse Quotient.mk'' φ) + (h : ∀ b : X, MulAction.stabilizer G b = ⊥) : + X ≃ Quotient (MulAction.orbitRel G X) × G := + (MulAction.selfEquivSigmaOrbitsQuotientStabilizer' G X hφ).trans <| (Equiv.sigmaCongrRight <| fun _ ↦ - (Subgroup.quotientEquivOfEq (h _)).trans (QuotientGroup.quotientEquivSelf α)).trans <| + (Subgroup.quotientEquivOfEq (h _)).trans (QuotientGroup.quotientEquivSelf G)).trans <| Equiv.sigmaEquivProd _ _ -/-- If `α` acts freely on `β`, `β` is equivalent to the product of the quotient of `β` by `α` and -`α`. -/ +/-- If `G` acts freely on `X`, `X` is equivalent to the product of the quotient of `X` by `G` and +`G`. -/ @[to_additive selfEquivOrbitsQuotientProd - /-- If `α` acts freely on `β`, `β` is equivalent to the product of the quotient of `β` by -`α` and `α`. -/] -noncomputable def selfEquivOrbitsQuotientProd (h : ∀ b : β, MulAction.stabilizer α b = ⊥) : - β ≃ Quotient (MulAction.orbitRel α β) × α := + /-- If `G` acts freely on `X`, `X` is equivalent to the product of the quotient of `X` by +`G` and `G`. -/] +noncomputable def selfEquivOrbitsQuotientProd (h : ∀ b : X, MulAction.stabilizer G b = ⊥) : + X ≃ Quotient (MulAction.orbitRel G X) × G := MulAction.selfEquivOrbitsQuotientProd' Quotient.out_eq' h end MulAction diff --git a/Mathlib/GroupTheory/IsPerfect.lean b/Mathlib/GroupTheory/IsPerfect.lean index 878b92ee0cd9bc..ca51df8499158c 100644 --- a/Mathlib/GroupTheory/IsPerfect.lean +++ b/Mathlib/GroupTheory/IsPerfect.lean @@ -104,4 +104,41 @@ lemma ofSurjective [IsPerfect G] (hf : Function.Surjective f) : IsPerfect G' := instance instQuotientSubgroup [H.Normal] [IsPerfect G] : IsPerfect (G ⧸ H) := ofSurjective (QuotientGroup.mk'_surjective H) +variable (G) in +@[simp] +theorem derivedSeries_eq_top [IsPerfect G] (n : ℕ) : derivedSeries G n = ⊤ := by + match n with + | 0 => simp + | n + 1 => + rw [derivedSeries_succ, derivedSeries_eq_top, commutator_eq_self] + +@[simp] +theorem lowerCentralSeries_eq_top (H : Subgroup G) [IsPerfect H] (n : ℕ) : + H.lowerCentralSeries n = H := by + match n with + | 0 => simp + | n + 1 => + rw [Subgroup.lowerCentralSeries_succ, lowerCentralSeries_eq_top, commutator_eq_self] + +variable (G) in +@[simp] +theorem upperCentralSeries_eq_center [IsPerfect G] {n : ℕ} (hn : n ≠ 0) : + Subgroup.upperCentralSeries G n = center G := by + rw [← Subgroup.upperCentralSeries_one, eq_comm] + apply Subgroup.upperCentralSeries.eq_ge_of_eq_succ <| by lia + apply le_antisymm <| Subgroup.upperCentralSeries_mono G one_le_two + rw [Subgroup.upperCentralSeries_one, ← commutator_top_right_eq_bot_iff_le_center, + ← commutator_eq_top, commutator_comm, commutator_def] + suffices ⁅⁅Subgroup.upperCentralSeries G 2, ⊤⁆, ⊤⁆ = ⊥ from + commutator_commutator_eq_bot_of_rotate (by simpa [commutator_comm]) this + rw [commutator_top_right_eq_bot_iff_le_center, ← Subgroup.upperCentralSeries_one] + apply commutator_upperCentralSeries_top_le + +variable (G) in +/-- **Grün's lemma** -/ +theorem center_quotient_center_eq_bot [IsPerfect G] : center (G ⧸ center G) = ⊥ := by + rw [← Subgroup.upperCentralSeries_one (G ⧸ center G), + ← comap_eq_ker_of_surjective <| QuotientGroup.mk'_surjective _, QuotientGroup.ker_mk', + Subgroup.comap_upperCentralSeries_quotient_center, upperCentralSeries_eq_center G <| by lia] + end Group.IsPerfect diff --git a/Mathlib/GroupTheory/Nilpotent.lean b/Mathlib/GroupTheory/Nilpotent.lean index 65630cb084419b..f8c36afe217b70 100644 --- a/Mathlib/GroupTheory/Nilpotent.lean +++ b/Mathlib/GroupTheory/Nilpotent.lean @@ -218,6 +218,14 @@ theorem mem_upperCentralSeries_succ_iff {n : ℕ} {x : G} : x ∈ upperCentralSeries G (n + 1) ↔ ∀ y : G, ⁅x, y⁆ ∈ upperCentralSeries G n := Iff.rfl +variable (G) in +@[to_additive] +theorem commutator_upperCentralSeries_top_le (n : ℕ) : + ⁅upperCentralSeries G (n + 1), ⊤⁆ ≤ upperCentralSeries G n := by + apply closure_le _ |>.mpr + rintro _ ⟨h, hh, g, _, rfl⟩ + exact mem_upperCentralSeries_succ_iff.mp hh g + @[to_additive (attr := simp)] lemma comap_upperCentralSeries {H : Type*} [Group H] (e : H ≃* G) : ∀ n, (upperCentralSeries G n).comap e = upperCentralSeries H n diff --git a/Mathlib/GroupTheory/OrderOfElement.lean b/Mathlib/GroupTheory/OrderOfElement.lean index a3c866e905e6ff..b7ee3e5669e10e 100644 --- a/Mathlib/GroupTheory/OrderOfElement.lean +++ b/Mathlib/GroupTheory/OrderOfElement.lean @@ -275,6 +275,17 @@ theorem orderOf_dvd_iff_pow_eq_one {n : ℕ} : orderOf x ∣ n ↔ x ^ n = 1 := ⟨fun h => by rw [← pow_mod_orderOf, Nat.mod_eq_zero_of_dvd h, _root_.pow_zero], orderOf_dvd_of_pow_eq_one⟩ +/-- If `x ^ p = 1` for some odd `p`, then every power of `x` is an even power of `x`. -/ +@[to_additive /-- If `p • x = 0` for some odd `p`, then every multiple of `x` is an even +multiple of `x`. -/] +theorem exists_pow_eq_pow_two_mul {p : ℕ} (hx : x ^ p = 1) (hp : Odd p) (n : ℕ) : + ∃ m, x ^ n = x ^ (2 * m) := by + obtain ⟨r, rfl⟩ := hp + have key : x ^ (2 * (r + 1)) = x := by + have h2 : 2 * (r + 1) = 2 * r + 1 + 1 := by omega + rw [h2, pow_succ, hx, one_mul] + exact ⟨(r + 1) * n, by rw [← mul_assoc, pow_mul, key]⟩ + @[to_additive addOrderOf_smul_dvd] theorem orderOf_pow_dvd (n : ℕ) : orderOf (x ^ n) ∣ orderOf x := by rw [orderOf_dvd_iff_pow_eq_one, pow_right_comm, pow_orderOf_eq_one, one_pow] diff --git a/Mathlib/GroupTheory/Perm/Finite.lean b/Mathlib/GroupTheory/Perm/Finite.lean index 929c5833c09478..4fbb18d909c321 100644 --- a/Mathlib/GroupTheory/Perm/Finite.lean +++ b/Mathlib/GroupTheory/Perm/Finite.lean @@ -125,7 +125,6 @@ theorem perm_mapsTo_inl_iff_mapsTo_inr {m n : Type*} [Finite m] [Finite n] (σ : obtain ⟨y, hy⟩ := h ⟨r, rfl⟩ grind -set_option backward.isDefEq.respectTransparency false in theorem mem_sumCongrHom_range_of_perm_mapsTo_inl {m n : Type*} [Finite m] [Finite n] {σ : Perm (m ⊕ n)} (h : Set.MapsTo σ (Set.range Sum.inl) (Set.range Sum.inl)) : σ ∈ (sumCongrHom m n).range := by @@ -143,14 +142,11 @@ theorem mem_sumCongrHom_range_of_perm_mapsTo_inl {m n : Type*} [Finite m] [Finit rw [Perm.sumCongrHom_apply] ext (a | b) · rw [Equiv.sumCongr_apply, Sum.map_inl, permCongr_apply, Equiv.symm_symm, - apply_ofInjective_symm Sum.inl_injective] - rw [ofInjective_apply, Subtype.coe_mk, Subtype.coe_mk] - dsimp [Set.range] - rw [subtypePerm_apply] + apply_ofInjective_symm Sum.inl_injective, ofInjective_apply] + rfl · rw [Equiv.sumCongr_apply, Sum.map_inr, permCongr_apply, Equiv.symm_symm, apply_ofInjective_symm Sum.inr_injective, ofInjective_apply] - dsimp [Set.range] - rw [subtypePerm_apply] + rfl nonrec theorem Disjoint.orderOf {σ τ : Perm α} (hστ : Disjoint σ τ) : orderOf (σ * τ) = Nat.lcm (orderOf σ) (orderOf τ) := diff --git a/Mathlib/GroupTheory/RegularWreathProduct.lean b/Mathlib/GroupTheory/RegularWreathProduct.lean index b82ac380fbfac4..593c7448dd641f 100644 --- a/Mathlib/GroupTheory/RegularWreathProduct.lean +++ b/Mathlib/GroupTheory/RegularWreathProduct.lean @@ -213,9 +213,9 @@ theorem IteratedWreathProduct.card [Finite G] : Nat.card (IteratedWreathProduct variable [Group G] instance : Group (IteratedWreathProduct G n) := by - induction n with - | zero => rw [IteratedWreathProduct_zero]; infer_instance - | succ n ih => rw [IteratedWreathProduct_succ]; infer_instance + induction n with + | zero => rw [IteratedWreathProduct_zero]; infer_instance + | succ n ih => rw [IteratedWreathProduct_succ]; infer_instance /-- The homomorphism from `IteratedWreathProduct G n` to `Perm (Fin n → G)`. -/ def iteratedWreathToPermHom (G : Type*) [Group G] : diff --git a/Mathlib/GroupTheory/Schreier.lean b/Mathlib/GroupTheory/Schreier.lean index f368c740032423..1133fbe3ba7e88 100644 --- a/Mathlib/GroupTheory/Schreier.lean +++ b/Mathlib/GroupTheory/Schreier.lean @@ -87,8 +87,8 @@ theorem closure_mul_image_mul_eq_top rw [mul_assoc, ← inv_inv s, ← mul_inv_rev, inv_inv] exact hR.toRightFun_mul_inv_mem (r * s⁻¹) -/-- **Schreier's Lemma**: If `R : Set G` is a `rightTransversal` of `H : Subgroup G` - with `1 ∈ R`, and if `G` is generated by `S : Set G`, then `H` is generated by the `Set` +/-- **Schreier's Lemma**: If `R : Set G` and `H : Subgroup G` are complements with `1 ∈ R`, and if +`G` is generated by `S : Set G`, then `H` is generated by the `Set` `(R * S).image (fun g ↦ g * (hR.toRightFun g)⁻¹)`. -/ @[wikidata Q3229345] theorem closure_mul_image_eq (hR : IsComplement H R) (hR1 : (1 : G) ∈ R) @@ -108,8 +108,8 @@ theorem closure_mul_image_eq (hR : IsComplement H R) (hR1 : (1 : G) ∈ R) · rw [Subtype.coe_mk, inv_one, mul_one] exact (H.mul_mem_cancel_left (hU hg)).mp hh -/-- **Schreier's Lemma**: If `R : Set G` is a `rightTransversal` of `H : Subgroup G` - with `1 ∈ R`, and if `G` is generated by `S : Set G`, then `H` is generated by the `Set` +/-- **Schreier's Lemma**: If `R : Set G` and `H : Subgroup G` are complements with `1 ∈ R`, and if +`G` is generated by `S : Set G`, then `H` is generated by the `Set` `(R * S).image (fun g ↦ g * (hR.toRightFun g)⁻¹)`. -/ theorem closure_mul_image_eq_top (hR : IsComplement H R) (hR1 : (1 : G) ∈ R) (hS : closure S = ⊤) : closure ((R * S).image fun g => @@ -117,8 +117,8 @@ theorem closure_mul_image_eq_top (hR : IsComplement H R) (hR1 : (1 : G) ∈ R) rw [eq_top_iff, ← map_subtype_le_map_subtype, MonoidHom.map_closure, Set.image_image] exact (map_subtype_le ⊤).trans (ge_of_eq (closure_mul_image_eq hR hR1 hS)) -/-- **Schreier's Lemma**: If `R : Finset G` is a `rightTransversal` of `H : Subgroup G` - with `1 ∈ R`, and if `G` is generated by `S : Finset G`, then `H` is generated by the `Finset` +/-- **Schreier's Lemma**: If `R : Finset G` and `H : Subgroup G` are complements with `1 ∈ R`, and +if `G` is generated by `S : Finset G`, then `H` is generated by the `Finset` `(R * S).image (fun g ↦ g * (hR.toRightFun g)⁻¹)`. -/ theorem closure_mul_image_eq_top' [DecidableEq G] {R S : Finset G} (hR : IsComplement (H : Set G) R) (hR1 : (1 : G) ∈ R) diff --git a/Mathlib/GroupTheory/SpecificGroups/Alternating/MaximalSubgroups.lean b/Mathlib/GroupTheory/SpecificGroups/Alternating/MaximalSubgroups.lean index 06333463c6095b..f140de24f68cb0 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Alternating/MaximalSubgroups.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Alternating/MaximalSubgroups.lean @@ -260,7 +260,7 @@ theorem isCoatom_stabilizer_of_ncard_lt_ncard_compl {s : Set α} IsBlock.subsingleton_of_ssubset_compl_of_stabilizer_alternatingGroup_le h0 (hBsc.ssubset_of_ne (by aesop)) -- uses Step 1 hG.le hB - -- Step 3 : A block contained in `s` is a subsingleton + -- Step 3 : A block contained in `s` is a subsingleton have hB_not_le_s (B : Set α) (hB : IsBlock G B) (hBs : B ⊆ s) : B.Subsingleton := have : IsPreprimitive (stabilizer G s) s := diff --git a/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean b/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean index 04744ce257b361..673b7378410d62 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Cyclic.lean @@ -599,7 +599,6 @@ noncomputable def IsCyclic.mulAutMulEquiv [Group G] [h : IsCyclic G] : variable (G) in theorem IsCyclic.card_mulAut [Group G] [Finite G] [h : IsCyclic G] : Nat.card (MulAut G) = Nat.totient (Nat.card G) := by - have : NeZero (Nat.card G) := ⟨Nat.card_pos.ne'⟩ rw [← ZMod.card_units_eq_totient, ← Nat.card_eq_fintype_card] exact Nat.card_congr (mulAutMulEquiv G) @@ -796,7 +795,6 @@ theorem not_isAddCyclic_prod_of_infinite_nontrivial (M N : Type*) [AddGroup M] [ have := isAddCyclic_of_surjective (f.prodMap f) (Prod.map_surjective.mpr ⟨hf, hf⟩) simpa using coprime_card_of_isAddCyclic_prod (ZMod 2) (ZMod 2) let ZN := ZMod (Nat.card N) - have : NeZero (Nat.card N) := ⟨Nat.card_pos.ne'⟩ have := isAddCyclic_of_surjective ((ZMod.castHom (dvd_zero _) ZN).toAddMonoidHom.prodMap (.id ZN)) (Prod.map_surjective.mpr ⟨ZMod.castHom_surjective (dvd_zero _), Function.surjective_id⟩) exact Finite.one_lt_card (α := N).ne' (by simpa [ZN] using coprime_card_of_isAddCyclic_prod ZN ZN) diff --git a/Mathlib/GroupTheory/SpecificGroups/Cyclic/Basic.lean b/Mathlib/GroupTheory/SpecificGroups/Cyclic/Basic.lean index cfd567da7754a6..77641c60d15f4b 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Cyclic/Basic.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Cyclic/Basic.lean @@ -414,7 +414,6 @@ theorem IsCyclic.image_range_card (ha : ∀ x : α, x ∈ zpowers a) : @[to_additive] lemma IsCyclic.ext [Finite G] [IsCyclic G] {d : ℕ} {a b : ZMod d} (hGcard : Nat.card G = d) (h : ∀ t : G, t ^ a.val = t ^ b.val) : a = b := by - have : NeZero (Nat.card G) := ⟨Nat.card_pos.ne'⟩ obtain ⟨g, hg⟩ := IsCyclic.exists_generator (α := G) specialize h g subst hGcard diff --git a/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean b/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean index cf44529992f105..8fb79b38b4cf4a 100644 --- a/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean +++ b/Mathlib/GroupTheory/SpecificGroups/Quaternion.lean @@ -168,7 +168,7 @@ instance [NeZero n] : Fintype (QuaternionGroup n) := Fintype.ofEquiv _ fintypeHelper instance : Nontrivial (QuaternionGroup n) := - ⟨⟨a 0, xa 0, by simp [- a_zero]⟩⟩ + ⟨⟨a 0, xa 0, by simp [-a_zero]⟩⟩ /-- If `0 < n`, then `QuaternionGroup n` has `4n` elements. -/ diff --git a/Mathlib/GroupTheory/Subgroup/Centralizer.lean b/Mathlib/GroupTheory/Subgroup/Centralizer.lean index f0954a435f7858..110e7845251dea 100644 --- a/Mathlib/GroupTheory/Subgroup/Centralizer.lean +++ b/Mathlib/GroupTheory/Subgroup/Centralizer.lean @@ -74,6 +74,10 @@ theorem centralizer_le {s t : Set G} (h : s ⊆ t) : centralizer t ≤ centraliz theorem centralizer_eq_top_iff_subset {s : Set G} : centralizer s = ⊤ ↔ s ⊆ center G := SetLike.ext'_iff.trans Set.centralizer_eq_top_iff_subset +@[to_additive (attr := simp)] +theorem centralizer_center : centralizer (center G : Set G) = ⊤ := + centralizer_eq_top_iff_subset.mpr le_rfl + @[to_additive] theorem map_centralizer_le_centralizer_image (s : Set G) (f : G →* G') : (Subgroup.centralizer s).map f ≤ Subgroup.centralizer (f '' s) := by diff --git a/Mathlib/GroupTheory/Subgroup/Simple.lean b/Mathlib/GroupTheory/Subgroup/Simple.lean index 71df18745f6fcd..9222b5b591229a 100644 --- a/Mathlib/GroupTheory/Subgroup/Simple.lean +++ b/Mathlib/GroupTheory/Subgroup/Simple.lean @@ -39,12 +39,16 @@ class IsSimpleGroup : Prop extends Nontrivial G where /-- Any normal subgroup is either `⊥` or `⊤` -/ eq_bot_or_eq_top_of_normal : ∀ H : Subgroup G, H.Normal → H = ⊥ ∨ H = ⊤ +attribute [instance 100] IsSimpleGroup.toNontrivial + /-- An `AddGroup` is simple when it has exactly two normal `AddSubgroup`s. -/ @[mk_iff] class IsSimpleAddGroup : Prop extends Nontrivial A where /-- Any normal additive subgroup is either `⊥` or `⊤` -/ eq_bot_or_eq_top_of_normal : ∀ H : AddSubgroup A, H.Normal → H = ⊥ ∨ H = ⊤ +attribute [instance 100] IsSimpleAddGroup.toNontrivial + attribute [to_additive existing] IsSimpleGroup isSimpleGroup_iff variable {G} {A} diff --git a/Mathlib/GroupTheory/Submonoid/Inverses.lean b/Mathlib/GroupTheory/Submonoid/Inverses.lean index 847d5ddb31f104..d4efa62017f0eb 100644 --- a/Mathlib/GroupTheory/Submonoid/Inverses.lean +++ b/Mathlib/GroupTheory/Submonoid/Inverses.lean @@ -56,6 +56,7 @@ theorem _root_.IsUnit.submonoid.coe_inv [Monoid M] (x : IsUnit.submonoid M) : @[deprecated (since := "2026-05-24")] alias _root_.AddSubmonoid.IsUnit.Submonoid.coe_neg := IsAddUnit.addSubmonoid.coe_neg +set_option linter.dupNamespace false in @[to_additive existing, deprecated (since := "2026-05-24")] alias IsUnit.Submonoid.coe_inv := IsUnit.submonoid.coe_inv diff --git a/Mathlib/Init.lean b/Mathlib/Init.lean index 81b05422e07d29..e49829ccc5766f 100644 --- a/Mathlib/Init.lean +++ b/Mathlib/Init.lean @@ -132,7 +132,7 @@ run_cmd liftTermElabM do let DefinedInScripts : Array Name := #[`linter.checkInitImports, `linter.allScriptsDocumented] let env ← getEnv - let ls := linterSetsExt.getEntries env + let ls := (linterSetsExt.getState env).localEntries let some (_, mlLinters) := ls.find? (·.1 == ``linter.mathlibStandardSet) | throwError m!"'linter.mathlibStandardSet' is not defined." let some (_, nrLinters) := ls.find? (·.1 == ``linter.nightlyRegressionSet) | diff --git a/Mathlib/Lean/MessageData/ForExprs.lean b/Mathlib/Lean/MessageData/ForExprs.lean index a67e574dbe10fc..93d155149f7d7a 100644 --- a/Mathlib/Lean/MessageData/ForExprs.lean +++ b/Mathlib/Lean/MessageData/ForExprs.lean @@ -68,6 +68,7 @@ where | .ofFormatWithInfos fwi => do let some ppCtx := ctx?.map (mkPPContext nctx) | return .yield s goFmt ppCtx fwi.infos s fwi.fmt + | .ofOriginatingSyntax _ m => go nctx ctx? s m /-- Iterate over the tags of a `Format` using `f`. -/ goFmt (ppCtx : PPContext) (infos) (s : σ) : Format → m (ForInStep σ) | .tag n fmt => do diff --git a/Mathlib/Lean/Meta/KAbstractPositions.lean b/Mathlib/Lean/Meta/KAbstractPositions.lean index 836b353cdcef16..55291bb6c9111e 100644 --- a/Mathlib/Lean/Meta/KAbstractPositions.lean +++ b/Mathlib/Lean/Meta/KAbstractPositions.lean @@ -33,7 +33,6 @@ namespace Lean.Meta /-- Return the positions that `kabstract` would abstract for pattern `p` in expression `e`. i.e. the positions that unify with `p`. -/ def kabstractPositions (p e : Expr) : MetaM (Array SubExpr.Pos) := do - let e ← instantiateMVars e let mctx ← getMCtx let pHeadIdx := p.toHeadIndex let pNumArgs := p.headNumArgs diff --git a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean index 6baef24ebebd58..4650ebd053cd21 100644 --- a/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean +++ b/Mathlib/Lean/Meta/RefinedDiscrTree/Encode.lean @@ -134,8 +134,8 @@ private def etaPossibilities (e : Expr) (lambdas : List FVarId) (root : Bool) if isStarWithArg (.fvar fvarId) a && !f.getAppFn.isMVar then etaPossibilities f lambdas root entry else - return [] - | _, _ => return []) + pure [] + | _, _ => pure []) where /-- Check whether the expression is represented by `Key.star` and has `arg` as an argument. -/ isStarWithArg (arg : Expr) : Expr → Bool diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean index 05e9a6328155cd..5c600a01296781 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/AffineEquiv.lean @@ -427,6 +427,7 @@ def vaddConst (b : P₁) : V₁ ≃ᵃ[k] P₁ where map_vadd' _ _ := add_vadd _ _ _ /-- `p' ↦ p -ᵥ p'` as an equivalence. -/ +@[simps! linear apply symm_apply] def constVSub (p : P₁) : P₁ ≃ᵃ[k] V₁ where toEquiv := Equiv.constVSub p linear := LinearEquiv.neg k @@ -509,14 +510,18 @@ variable {P₁} open Function -/-- Point reflection in `x` as a permutation. -/ +/-- The affine equivalence given by reflection about the point `x`. +This is `Equiv.pointReflection` as an `AffineEquiv`. -/ def pointReflection (x : P₁) : P₁ ≃ᵃ[k] P₁ := (constVSub k x).trans (vaddConst k x) -@[simp] lemma pointReflection_apply_eq_equivPointReflection_apply (x y : P₁) : - pointReflection k x y = Equiv.pointReflection x y := +@[simp] +lemma coe_pointReflection (x y : P₁) : pointReflection k x y = Equiv.pointReflection x y := rfl +@[deprecated (since := "2026-06-22")] +alias pointReflection_apply_eq_equivPointReflection_apply := coe_pointReflection + theorem pointReflection_apply (x y : P₁) : pointReflection k x y = (x -ᵥ y) +ᵥ x := rfl diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean index 5874d92b08e4bd..bec793d3828b41 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Basic.lean @@ -820,6 +820,7 @@ variable [AffineSpace V P] /-- Two affine subspaces are parallel if one is related to the other by adding the same vector to all points. -/ +@[wikidata Q53875] def Parallel (s₁ s₂ : AffineSubspace k P) : Prop := ∃ v : V, s₂ = s₁.map (constVAdd k P v) diff --git a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Shift.lean b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Shift.lean index 37edd698fd6839..1fb399ba99ebad 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Shift.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/AffineSubspace/Shift.lean @@ -5,7 +5,7 @@ Authors: Weiyi Wang -/ module -public import Mathlib.LinearAlgebra.AffineSpace.AffineSubspace.Basic +public import Mathlib.LinearAlgebra.AffineSpace.Simplex.Basic /-! # Shifting an affine subspace towards a point @@ -97,6 +97,50 @@ theorem shift_one (s : AffineSubspace k P) (c : P) : s.shift c 1 = s := by have h : Nonempty s := by simpa using! h simp [shift, h] +/-- Consider a point `A` with barycentric coordinates associated to a collection of points `P`. +If the coordinate associated to one of the points `Pᵢ` is `r`, then the point `A` is on the span +of `P \ {Pᵢ}` shifted towards `Pᵢ` with parameter `1 - r`. -/ +theorem affineCombination_mem_shift {ι : Type*} [Fintype ι] [Nontrivial ι] + (p : ι → P) (i : ι) {w : ι → k} (hw : ∑ i, w i = 1) : + affineCombination k univ p w ∈ (affineSpan k <| p '' {i}ᶜ).shift (p i) (1 - w i) := by + cases subsingleton_or_nontrivial k + · suffices (affineSpan k <| p '' {i}ᶜ) = ⊤ by simp [this] + have : Subsingleton P := (AddTorsor.subsingleton_iff V P).mp <| Module.subsingleton k V + simp + classical + obtain ⟨j, hj⟩ := exists_ne i + rw [shift_eq ⟨p j, mem_affineSpan k <| Set.mem_image_of_mem _ hj⟩] + suffices ∃ q ∈ affineSpan k (p '' {i}ᶜ), w i • (p i -ᵥ p j) +ᵥ q = affineCombination k univ p w by + simpa + refine ⟨-(w i • (p i -ᵥ p j)) +ᵥ affineCombination k univ p w, ?_, by simp⟩ + rw [← affineCombination_piSingle k _ p (mem_univ i), + ← affineCombination_piSingle k _ p (mem_univ j), affineCombination_vsub, ← map_smul, ← map_neg, + weightedVSub_vadd_affineCombination] + refine affineCombination_mem_affineSpan_image ?_ (fun i' _ hi ↦ by aesop) _ + simp [sum_add_distrib, ← mul_sum, hw] + +/-- The iff version of `affineCombination_mem_shift` for affine independent points. -/ +theorem _root_.AffineIndependent.affineCombination_mem_shift_iff + {ι : Type*} [Fintype ι] [Nontrivial ι] {p : ι → P} + (h : AffineIndependent k p) (i : ι) {w : ι → k} (hw : ∑ i, w i = 1) (c : k) : + affineCombination k univ p w ∈ (affineSpan k <| p '' {i}ᶜ).shift (p i) c ↔ + w i = 1 - c := by + classical + refine ⟨?_, fun h ↦ by simpa [h] using affineCombination_mem_shift p i hw⟩ + obtain ⟨j, hj⟩ := exists_ne i + rw [shift_eq ⟨p j, mem_affineSpan k <| Set.mem_image_of_mem _ hj⟩] + suffices ∀ q ∈ affineSpan k (p '' {i}ᶜ), + (1 - c) • (p i -ᵥ p j) +ᵥ q = affineCombination k univ p w → w i = 1 - c by simpa + intro q hqmem heq + obtain ⟨t, w', ht, hw', rfl⟩ := eq_affineCombination_of_mem_affineSpan_image hqmem + have ht : (t : Set ι).indicator w' i = 0 := Set.indicator_of_notMem (by simpa using ht) w' + rw [affineCombination_indicator_subset _ _ t.subset_univ, + ← affineCombination_piSingle k _ p (mem_univ i), + ← affineCombination_piSingle k _ p (mem_univ j), affineCombination_vsub, ← map_smul, + weightedVSub_vadd_affineCombination, h.affineCombination_eq_iff_eq ?_ hw] at heq + · simpa [hj.symm, ht] using (heq i (mem_univ i)).symm + · simp [sum_add_distrib, sum_indicator_subset, ← mul_sum, hw'] + end Ring section CommRing @@ -130,3 +174,87 @@ theorem shift_eq_map_homothety (s : AffineSubspace k P) (c : P) {r : k} (hr : Is end CommRing end AffineSubspace + +namespace Affine.Simplex + +section Ring +variable [Ring k] [PartialOrder k] [IsOrderedAddMonoid k] [AddCommGroup V] [AddTorsor V P] + [Module k V] {n : ℕ} [NeZero n] (s : Affine.Simplex k P n) (i : Fin (n + 1)) + +/-- The base of a simplex shifted with parameter 0 intersects the closed interior only at the +vertex. -/ +theorem closedInterior_inter_shift_zero [ZeroLEOneClass k] : + s.closedInterior ∩ (affineSpan k <| s.points '' {i}ᶜ).shift (s.points i) 0 = + {s.points i} := by + refine subset_antisymm (fun p ⟨hp, hshift⟩ ↦ ?_) (by simp [s.point_mem_closedInterior i]) + obtain ⟨w, hw, rfl⟩ := eq_affineCombination_of_mem_affineSpan_of_fintype <| + s.closedInterior_subset_affineSpan hp + suffices w = Pi.single i 1 by simp [this] + rw [affineCombination_mem_closedInterior_iff hw] at hp + rw [SetLike.mem_coe, s.independent.affineCombination_mem_shift_iff i hw, sub_zero] at hshift + ext j + by_cases hj : j = i + · aesop + rw [← univ.sum_erase_add w (mem_univ i), hshift, add_eq_right, + sum_eq_zero_iff_of_nonneg fun j _ ↦ (hp j).1] at hw + simp [hw j (by simpa using hj), hj] + +/-- The base of a simplex shifted with parameter outside $[0, 1]$ does not intersect the closed +interior. -/ +theorem disjoint_closedInterior_shift {x : k} (hx : x < 0 ∨ 1 < x) : + Disjoint s.closedInterior <| (affineSpan k (s.points '' {i}ᶜ)).shift (s.points i) x := by + refine Set.disjoint_left.mpr fun p hleft hright ↦ ?_ + obtain ⟨w, hw, rfl⟩ := eq_affineCombination_of_mem_affineSpan_of_fintype <| + s.closedInterior_subset_affineSpan hleft + rw [SetLike.mem_coe, s.independent.affineCombination_mem_shift_iff i hw] at hright + rw [affineCombination_mem_closedInterior_iff hw] at hleft + grind + +end Ring + +section Field +variable [Field k] [LinearOrder k] [IsOrderedRing k] [AddCommGroup V] [Module k V] [AddTorsor V P] + +private theorem closedInterior_inter_shift_aux {n : ℕ} (i : Fin n) {x : k} (hxpos : 0 < x) + (hx1 : x ≤ 1) {w : Fin n → k} (hw : ∑ i, w i = 1) : + (∀ j, w j ∈ Set.Icc 0 1) ∧ w i = 1 - x ↔ + (∀ j, j ≠ i → x⁻¹ * w j ∈ Set.Icc 0 1) ∧ x⁻¹ * (w i - 1) + 1 = 0 := by + rw [show x⁻¹ * (w i - 1) + 1 = 0 ↔ w i = 1 - x by grind] + refine and_congr_left fun hi ↦ ⟨fun hj j hji ↦ ⟨?_, ?_⟩, fun hj ↦ ?_⟩ + · exact mul_nonneg (by simpa using hxpos.le) (hj j).1 + · rw [eq_sub_iff_add_eq, add_comm, ← eq_sub_iff_add_eq] at hi + rw [inv_mul_le_one₀ hxpos, hi, le_sub_iff_add_le, ← hw] + exact add_le_sum (fun i _ ↦ (hj i).1) (mem_univ j) (mem_univ i) hji + · suffices ∀ j, 0 ≤ w j from + fun j ↦ ⟨this j, hw ▸ Finset.single_le_sum (fun j _ ↦ this j) (mem_univ j)⟩ + intro j + by_cases hji : j = i <;> aesop + +/-- A parallel cross-section of a simplex is the image of the base under a homothety. -/ +theorem closedInterior_inter_shift_eq_homothety {n : ℕ} [NeZero n] (s : Affine.Simplex k P n) + (i : Fin (n + 1)) {x : k} (hx : x ∈ Set.Icc 0 1) : + s.closedInterior ∩ (affineSpan k (s.points '' {i}ᶜ)).shift (s.points i) x = + homothety (s.points i) x '' (s.faceOpposite i).closedInterior := by + rcases hx.1.eq_or_lt with hx0 | hxpos + · simpa [hx0.symm, nonempty_closedInterior] using s.closedInterior_inter_shift_zero i + ext p + by_cases hp : p ∈ affineSpan k (.range s.points) + · obtain ⟨w, hw, rfl⟩ := eq_affineCombination_of_mem_affineSpan_of_fintype hp + rw [Set.mem_inter_iff, SetLike.mem_coe, s.independent.affineCombination_mem_shift_iff i hw, + affineCombination_mem_closedInterior_iff hw, Set.mem_image] + simp_rw [AffineMap.homothety_eq_iff_of_mul_eq_one (mul_inv_cancel₀ hxpos.ne.symm), + univ.homothety_affineCombination _ _ (mem_univ i)] + simp only [↓existsAndEq, and_true] + rw [faceOpposite, affineCombination_mem_closedInterior_face_iff_mem_Icc, + closedInterior_inter_shift_aux i hxpos hx.2 hw] + · simp only [mem_compl, mem_singleton, not_not, forall_eq] + congrm (∀ j, (hj : _) → $(by simp [lineMap_apply, hj])) ∧ $(by simp [lineMap_apply]) + · simp [AffineMap.lineMap_apply, Finset.sum_add_distrib, ← Finset.mul_sum, + Finset.sum_sub_distrib, hw] + · apply iff_of_false (hp <| s.closedInterior_subset_affineSpan ·.1) + rintro ⟨q, hq, rfl⟩ + exact hp <| homothety_mem (mem_affineSpan _ (by simp)) _ <| + affineSpan_mono _ (by simp) ((s.faceOpposite i).closedInterior_subset_affineSpan hq) + +end Field +end Affine.Simplex diff --git a/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean b/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean index 4e8ea4567aa25a..9137bd70e99de2 100644 --- a/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean +++ b/Mathlib/LinearAlgebra/AffineSpace/Simplex/Basic.lean @@ -497,6 +497,10 @@ lemma point_mem_closedInterior [ZeroLEOneClass k] {n : ℕ} (s : Simplex k P n) intro j obtain rfl | hj := eq_or_ne j i <;> simp_all +lemma nonempty_closedInterior [ZeroLEOneClass k] {n : ℕ} (s : Simplex k P n) : + s.closedInterior.Nonempty := + ⟨s.points 0, s.point_mem_closedInterior 0⟩ + lemma interior_ssubset_closedInterior [ZeroLEOneClass k] {n : ℕ} (s : Simplex k P n) : s.interior ⊂ s.closedInterior := by rw [Set.ssubset_iff_exists] @@ -643,7 +647,7 @@ theorem closedInterior_face_ssubset_closedInterior [Nontrivial k] [ZeroLEOneClas apply (Set.ssubset_iff_of_subset (s.closedInterior_face_subset_closedInterior h)).mpr exact ⟨s.points a, s.point_mem_closedInterior a, fun hs ↦ ha (by simpa using hs)⟩ -theorem disjoint_interior_closedInterior_face [Nontrivial k] [ZeroLEOneClass k] {n : ℕ} +theorem disjoint_interior_closedInterior_face {n : ℕ} (s : Simplex k P n) {fs : Finset (Fin (n + 1))} (hfs : fs ≠ .univ) {m : ℕ} (h : #fs = m + 1) : Disjoint s.interior (s.face h).closedInterior := by refine Set.disjoint_left.mpr fun p hleft hright ↦ ?_ @@ -669,8 +673,8 @@ theorem closedInterior_faceOpposite_ssubset_closedInterior [Nontrivial k] [ZeroL (s.faceOpposite i).closedInterior ⊂ s.closedInterior := s.closedInterior_face_ssubset_closedInterior (by simp) _ -theorem disjoint_interior_closedInterior_faceOpposite [Nontrivial k] [ZeroLEOneClass k] {n : ℕ} - [NeZero n] (s : Simplex k P n) (i : Fin (n + 1)) : +theorem disjoint_interior_closedInterior_faceOpposite {n : ℕ} [NeZero n] + (s : Simplex k P n) (i : Fin (n + 1)) : Disjoint s.interior (s.faceOpposite i).closedInterior := s.disjoint_interior_closedInterior_face (by simp) _ @@ -705,7 +709,7 @@ theorem closedInterior_eq_interior_union [IsOrderedAddMonoid k] [ZeroLEOneClass · refine Set.union_subset s.interior_subset_closedInterior (Set.iUnion_subset fun i ↦ ?_) exact s.closedInterior_faceOpposite_subset_closedInterior i -theorem closedInterior_sdiff_interior [Nontrivial k] [IsOrderedAddMonoid k] [ZeroLEOneClass k] +theorem closedInterior_sdiff_interior [IsOrderedAddMonoid k] [ZeroLEOneClass k] {n : ℕ} [NeZero n] (s : Simplex k P n) : s.closedInterior \ s.interior = ⋃ i : Fin (n + 1), (s.faceOpposite i).closedInterior := by simpa [closedInterior_eq_interior_union] using diff --git a/Mathlib/LinearAlgebra/Basis/Defs.lean b/Mathlib/LinearAlgebra/Basis/Defs.lean index c66920d1a2c6eb..c4ff6d939ed635 100644 --- a/Mathlib/LinearAlgebra/Basis/Defs.lean +++ b/Mathlib/LinearAlgebra/Basis/Defs.lean @@ -6,6 +6,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Alexander Bentkamp module public import Mathlib.LinearAlgebra.Finsupp.LinearCombination +public import Mathlib.Tactic.CrossRefAttribute /-! # Bases @@ -85,6 +86,7 @@ To turn a linear independent family of vectors spanning `M` into a basis, use `B They are internally represented as linear equivs `M ≃ₗ[R] (ι →₀ R)`, available as `Basis.repr`. -/ +@[wikidata Q189569] structure Basis where /-- `Basis.ofRepr` constructs a basis given an assignment of coordinates to each vector. -/ ofRepr :: diff --git a/Mathlib/LinearAlgebra/Basis/MulOpposite.lean b/Mathlib/LinearAlgebra/Basis/MulOpposite.lean index e0f9bb47f3ff86..5661acfbf191f0 100644 --- a/Mathlib/LinearAlgebra/Basis/MulOpposite.lean +++ b/Mathlib/LinearAlgebra/Basis/MulOpposite.lean @@ -58,7 +58,7 @@ instance [Semiring R] [AddCommMonoid H] [Module R H] theorem rank [Semiring R] [StrongRankCondition R] [AddCommMonoid H] [Module R H] [Module.Free R H] : Module.rank R Hᵐᵒᵖ = Module.rank R H := - LinearEquiv.nonempty_equiv_iff_rank_eq.mp ⟨(opLinearEquiv R).symm⟩ + Module.nonempty_linearEquiv_iff_rank_eq.mp ⟨(opLinearEquiv R).symm⟩ theorem finrank [DivisionRing R] [AddCommGroup H] [Module R H] : Module.finrank R Hᵐᵒᵖ = Module.finrank R H := by diff --git a/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean b/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean index 6400c41c3c9ff0..d837239375be43 100644 --- a/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean +++ b/Mathlib/LinearAlgebra/BilinearForm/Orthogonal.lean @@ -7,6 +7,7 @@ module public import Mathlib.Algebra.GroupWithZero.NonZeroDivisors public import Mathlib.LinearAlgebra.BilinearForm.Properties +public import Mathlib.LinearAlgebra.SesquilinearForm.Orthogonal /-! # Bilinear form diff --git a/Mathlib/LinearAlgebra/Charpoly/Basic.lean b/Mathlib/LinearAlgebra/Charpoly/Basic.lean index e7e2ce6bd931a1..c3e7afb498ccc3 100644 --- a/Mathlib/LinearAlgebra/Charpoly/Basic.lean +++ b/Mathlib/LinearAlgebra/Charpoly/Basic.lean @@ -88,7 +88,7 @@ to the linear map itself, is zero. See `Matrix.aeval_self_charpoly` for the equivalent statement about matrices. -/ theorem aeval_self_charpoly : aeval f f.charpoly = 0 := by apply (LinearEquiv.map_eq_zero_iff (algEquivMatrix (chooseBasis R M)).toLinearEquiv).1 - rw [AlgEquiv.toLinearEquiv_apply, ← AlgEquiv.coe_algHom, ← Polynomial.aeval_algHom_apply _ _ _, + rw [AlgEquiv.toLinearEquiv_apply, ← AlgEquiv.coe_toAlgHom, ← Polynomial.aeval_algHom_apply _ _ _, charpoly_def] exact Matrix.aeval_self_charpoly _ diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean index b07967f77a8080..33ca6fe5da724c 100644 --- a/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean +++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Basic.lean @@ -5,10 +5,11 @@ Authors: Eric Wieser, Utensil Song -/ module -public import Mathlib.Algebra.RingQuot +public import Mathlib.RingTheory.Congruence.Hom public import Mathlib.LinearAlgebra.TensorAlgebra.Basic public import Mathlib.LinearAlgebra.QuadraticForm.Isometry public import Mathlib.LinearAlgebra.QuadraticForm.IsometryEquiv +public import Mathlib.Tactic.CrossRefAttribute /-! # Clifford Algebras @@ -62,12 +63,15 @@ The Clifford algebra of `M` is defined as the quotient modulo this relation. inductive Rel : TensorAlgebra R M → TensorAlgebra R M → Prop | of (m : M) : Rel (ι R m * ι R m) (algebraMap R _ (Q m)) +/-- `Rel` as a ring congruence, used to build the quotient. -/ +@[no_expose] def ringCon : RingCon (TensorAlgebra R M) := ringConGen (Rel Q) + end CliffordAlgebra /-- The Clifford algebra of an `R`-module `M` equipped with a `QuadraticForm` `Q`. -/ -def CliffordAlgebra := - RingQuot (CliffordAlgebra.Rel Q) +@[wikidata Q674689] +def CliffordAlgebra := CliffordAlgebra.ringCon Q |>.Quotient deriving Inhabited, Ring, Algebra R namespace CliffordAlgebra @@ -76,7 +80,7 @@ instance (priority := 900) instAlgebra' {R A M} [CommSemiring R] [AddCommGroup M [Algebra R A] [Module R M] [Module A M] (Q : QuadraticForm A M) [IsScalarTower R A M] : Algebra R (CliffordAlgebra Q) := - inferInstanceAs <| Algebra R (RingQuot _) + inferInstanceAs <| Algebra R (RingCon.Quotient _) -- verify there are no diamonds -- but doesn't work at `reducible_and_instances` https://github.com/leanprover-community/mathlib4/issues/10906 @@ -88,27 +92,25 @@ instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommGroup M] [CommRing [Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M] (Q : QuadraticForm A M) [IsScalarTower R A M] [IsScalarTower S A M] : SMulCommClass R S (CliffordAlgebra Q) := - RingQuot.instSMulCommClass _ + RingCon.instSMulCommClassQuotient _ instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommGroup M] [CommRing A] [SMul R S] [Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M] [IsScalarTower R A M] [IsScalarTower S A M] [IsScalarTower R S A] (Q : QuadraticForm A M) : IsScalarTower R S (CliffordAlgebra Q) := - RingQuot.instIsScalarTower _ + RingCon.instIsScalarTowerQuotient _ /-- The canonical linear map `M →ₗ[R] CliffordAlgebra Q`. -/ def ι : M →ₗ[R] CliffordAlgebra Q := - (RingQuot.mkAlgHom R _).toLinearMap.comp (TensorAlgebra.ι R) + (RingCon.mkₐ R _).toLinearMap.comp (TensorAlgebra.ι R) + +private theorem ι_apply (m : M) : + ι Q m = (TensorAlgebra.ι R m : CliffordAlgebra.ringCon Q |>.Quotient) := rfl /-- As well as being linear, `ι Q` squares to the quadratic form -/ @[simp] -theorem ι_sq_scalar (m : M) : ι Q m * ι Q m = algebraMap R _ (Q m) := by - rw [ι] - erw [LinearMap.comp_apply] - rw [AlgHom.toLinearMap_apply] - erw [← map_mul (RingQuot.mkAlgHom R (Rel Q))] - rw [RingQuot.mkAlgHom_rel R (Rel.of m), AlgHom.commutes] - rfl +theorem ι_sq_scalar (m : M) : ι Q m * ι Q m = algebraMap R _ (Q m) := + Quotient.sound <| RingCon.le_ringConGen _ _ (Rel.of m) variable {Q} {A : Type*} [Semiring A] [Algebra R A] @@ -126,21 +128,25 @@ from `CliffordAlgebra Q` to `A`. def lift : { f : M →ₗ[R] A // ∀ m, f m * f m = algebraMap _ _ (Q m) } ≃ (CliffordAlgebra Q →ₐ[R] A) where toFun f := - RingQuot.liftAlgHom R - ⟨TensorAlgebra.lift R (f : M →ₗ[R] A), fun x y (h : Rel Q x y) => by - induction h - rw [AlgHom.commutes, map_mul, TensorAlgebra.lift_ι_apply, f.prop]⟩ + RingCon.liftₐ (CliffordAlgebra.ringCon Q) + (TensorAlgebra.lift R (f : M →ₗ[R] A)) + (by + exact RingCon.ringConGen_le.2 fun x y (h : Rel Q x y) => by + induction h + simp [f.prop]) invFun F := ⟨F.toLinearMap.comp (ι Q), fun m => by rw [LinearMap.comp_apply, AlgHom.toLinearMap_apply, comp_ι_sq_scalar]⟩ left_inv f := by ext x - exact (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ x) + dsimp + exact (RingCon.liftₐ_mk _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ x) right_inv F := - RingQuot.ringQuot_ext' _ _ _ <| + RingCon.Quotient.hom_extₐ <| TensorAlgebra.hom_ext <| - LinearMap.ext fun x ↦ - (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ _) + LinearMap.ext fun x ↦ by + dsimp + exact (RingCon.liftₐ_mk _ _ _ _).trans (TensorAlgebra.lift_ι_apply _ _) @[simp] theorem ι_comp_lift (f : M →ₗ[R] A) (cond : ∀ m, f m * f m = algebraMap _ _ (Q m)) : diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean index df8a439744af44..fedb6cf5ebf965 100644 --- a/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean +++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Equivs.lean @@ -355,12 +355,14 @@ protected def equiv : CliffordAlgebra (0 : QuadraticForm R R) ≃ₐ[R] R[ε] := (by ext : 1; simp) (by ext : 2; simp) @[simp] -theorem equiv_ι (r : R) : CliffordAlgebraDualNumber.equiv (ι (R := R) _ r) = r • ε := - (lift_ι_apply _ _ r).trans (inr_eq_smul_eps _) +theorem equiv_ι (r : R) : CliffordAlgebraDualNumber.equiv (ι (R := R) _ r) = r • ε := by + dsimp [CliffordAlgebraDualNumber.equiv, AlgEquiv.ofAlgHom] + exact (lift_ι_apply _ _ r).trans (inr_eq_smul_eps _) @[simp] theorem equiv_symm_eps : - CliffordAlgebraDualNumber.equiv.symm (eps : R[ε]) = ι (0 : QuadraticForm R R) 1 := - DualNumber.lift_apply_eps _ + CliffordAlgebraDualNumber.equiv.symm (eps : R[ε]) = ι (0 : QuadraticForm R R) 1 := by + dsimp [CliffordAlgebraDualNumber.equiv, AlgEquiv.ofAlgHom] + exact DualNumber.lift_apply_eps _ end CliffordAlgebraDualNumber diff --git a/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean b/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean index f7c84a08497f7d..eb3939dd0375a8 100644 --- a/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean +++ b/Mathlib/LinearAlgebra/CliffordAlgebra/Even.lean @@ -203,11 +203,11 @@ theorem aux_one : aux f 1 = 1 := congr_arg Prod.fst (foldr_one _ _ _ _) @[simp] -theorem aux_ι (m₁ m₂ : M) : aux f ((even.ι Q).bilin m₁ m₂) = f.bilin m₁ m₂ := - (congr_arg Prod.fst (foldr_mul _ _ _ _ _ _)).trans - (by - rw [foldr_ι, foldr_ι] - exact mul_one _) +theorem aux_ι (m₁ m₂ : M) : aux f ((even.ι Q).bilin m₁ m₂) = f.bilin m₁ m₂ := by + rw [CliffordAlgebra.even.lift.aux_apply] + refine (congr_arg Prod.fst (foldr_mul Q (fFold f) _ _ _ _)).trans ?_ + rw [foldr_ι, foldr_ι] + exact mul_one _ @[simp] theorem aux_algebraMap (r) : diff --git a/Mathlib/LinearAlgebra/Complex/FiniteDimensional.lean b/Mathlib/LinearAlgebra/Complex/FiniteDimensional.lean index e96472678489d0..f100705876f12b 100644 --- a/Mathlib/LinearAlgebra/Complex/FiniteDimensional.lean +++ b/Mathlib/LinearAlgebra/Complex/FiniteDimensional.lean @@ -76,6 +76,6 @@ lemma Complex.rank_rat_complex : Module.rank ℚ ℂ = continuum := by /-- `ℂ` and `ℝ` are isomorphic as vector spaces over `ℚ`, or equivalently, as additive groups. -/ theorem Complex.nonempty_linearEquiv_real : Nonempty (ℂ ≃ₗ[ℚ] ℝ) := - LinearEquiv.nonempty_equiv_iff_rank_eq.mpr <| by simp + Module.nonempty_linearEquiv_iff_rank_eq.mpr <| by simp end Rational diff --git a/Mathlib/LinearAlgebra/Complex/Module.lean b/Mathlib/LinearAlgebra/Complex/Module.lean index 6b9d1cdd32c577..26fb146affe3e9 100644 --- a/Mathlib/LinearAlgebra/Complex/Module.lean +++ b/Mathlib/LinearAlgebra/Complex/Module.lean @@ -491,6 +491,19 @@ lemma ComplexStarModule.ext_iff {x y : A} : x = y ↔ ℜ x = ℜ y ∧ ℑ x = mp := by grind mpr h := ext h.1 h.2 +section StarHomClass + +variable {B F : Type*} [AddCommGroup B] [Module ℂ B] [StarAddMonoid B] [StarModule ℂ B] + [FunLike F A B] [StarHomClass F A B] [LinearMapClass F ℂ A B] + +lemma map_realPart (f : F) (x : A) : f (ℜ x) = ℜ (f x) := by + simp [realPart_apply_coe, ← Complex.coe_smul, map_star] + +lemma map_imaginaryPart (f : F) (x : A) : f (ℑ x) = ℑ (f x) := by + simp [imaginaryPart_apply_coe, ← Complex.coe_smul, map_star] + +end StarHomClass + @[simp] theorem ker_imaginaryPart : imaginaryPart.ker = selfAdjoint.submodule ℝ A := by ext x @@ -638,4 +651,14 @@ lemma mem_unitary_iff_isStarNormal_and_realPart_sq_add_imaginaryPart_sq_eq_one [ exact ⟨this, by simp [sq, ← star_mul_self_eq_realPart_sq_add_imaginaryPart_sq x, h]⟩ · simp [← hx.star_comm_self.eq, star_mul_self_eq_realPart_sq_add_imaginaryPart_sq, ← sq, h] +instance {F E A : Type*} [AddCommGroup E] [PartialOrder E] + [StarAddMonoid E] [SelfAdjointDecompose E] [Module ℂ E] [StarModule ℂ E] + [NonUnitalRing A] [PartialOrder A] [StarRing A] + [StarOrderedRing A] [Module ℂ A] [StarModule ℂ A] + [FunLike F E A] [OrderHomClass F E A] [LinearMapClass F ℂ E A] : + StarHomClass F E A where + map_star φ x := by + rw [← realPart_add_I_smul_imaginaryPart x] + simp [(ℜ x).2.map' φ, IsSelfAdjoint.star_eq, (ℑ x).2.map' φ] + end RealImaginaryPart diff --git a/Mathlib/LinearAlgebra/Determinant.lean b/Mathlib/LinearAlgebra/Determinant.lean index 80c91075db806c..8390d28ac03e97 100644 --- a/Mathlib/LinearAlgebra/Determinant.lean +++ b/Mathlib/LinearAlgebra/Determinant.lean @@ -17,6 +17,7 @@ public import Mathlib.RingTheory.Finiteness.Cardinality public import Mathlib.Tactic.FieldSimp import Mathlib.LinearAlgebra.GeneralLinearGroup.AlgEquiv +import Mathlib.RingTheory.SimpleRing.Matrix /-! # Determinant of families of vectors @@ -268,7 +269,7 @@ theorem det_zero' {ι : Type*} [Finite ι] [Nonempty ι] (b : Basis ι A M) : LinearMap.det (0 : M →ₗ[A] M) = 0 := by haveI := Classical.decEq ι cases nonempty_fintype ι - rwa [← det_toMatrix b, map_zero, det_zero] + rw [← det_toMatrix b, map_zero, det_zero] /-- In a finite-dimensional vector space, the zero map has determinant `1` in dimension `0`, and `0` otherwise. We give a formula that also works in infinite dimension, where we define @@ -474,11 +475,12 @@ end LinearEquiv LinearMap.det_map ((Matrix.toLinAlgEquiv'.symm.trans (AlgEquivClass.toAlgEquiv f)).trans Matrix.toLinAlgEquiv') x.toLin' --- TODO: show `(f x).det = x.det` for when `f : Matrix m m K →ₐ[K] Matrix m m K` --- (using Skolem-Noether) -proof_wanted Matrix.det_map' {K m F : Type*} [Field K] [Fintype m] [DecidableEq m] +@[simp] theorem Matrix.det_map' {K m F : Type*} [Field K] [Fintype m] [DecidableEq m] [FunLike F (Matrix m m K) (Matrix m m K)] [AlgHomClass F K _ _] (f : F) (x : Matrix m m K) : - (f x).det = x.det + (f x).det = x.det := by + by_cases! Nonempty m + · exact det_map (AlgEquiv.ofBijective _ (AlgHomClass.toAlgHom f).bijective) x + · simp /-- The determinants of a `LinearEquiv` and its inverse multiply to 1. -/ @[simp] diff --git a/Mathlib/LinearAlgebra/Dimension/Free.lean b/Mathlib/LinearAlgebra/Dimension/Free.lean index 865262acab6c98..e61fdf32557a31 100644 --- a/Mathlib/LinearAlgebra/Dimension/Free.lean +++ b/Mathlib/LinearAlgebra/Dimension/Free.lean @@ -14,7 +14,7 @@ public import Mathlib.SetTheory.Cardinal.Finsupp # Rank of free modules ## Main result -- `LinearEquiv.nonempty_equiv_iff_lift_rank_eq`: +- `Module.nonempty_linearEquiv_iff_lift_rank_eq`: Two free modules are isomorphic iff they have the same dimension. - `Module.finBasis`: An arbitrary basis of a finite free module indexed by `Fin n` given `finrank R M = n`. @@ -193,15 +193,21 @@ def LinearEquiv.ofRankEq (cond : Module.rank R M = Module.rank R M₁) : M ≃ end /-- Two vector spaces are isomorphic if and only if they have the same dimension. -/ -theorem LinearEquiv.nonempty_equiv_iff_lift_rank_eq : Nonempty (M ≃ₗ[R] M') ↔ +theorem Module.nonempty_linearEquiv_iff_lift_rank_eq : Nonempty (M ≃ₗ[R] M') ↔ Cardinal.lift.{v'} (Module.rank R M) = Cardinal.lift.{v} (Module.rank R M') := ⟨fun ⟨h⟩ => LinearEquiv.lift_rank_eq h, fun h => nonempty_linearEquiv_of_lift_rank_eq h⟩ +@[deprecated (since := "2026-06-30")] +alias LinearEquiv.nonempty_equiv_iff_lift_rank_eq := Module.nonempty_linearEquiv_iff_lift_rank_eq + /-- Two vector spaces are isomorphic if and only if they have the same dimension. -/ -theorem LinearEquiv.nonempty_equiv_iff_rank_eq : +theorem Module.nonempty_linearEquiv_iff_rank_eq : Nonempty (M ≃ₗ[R] M₁) ↔ Module.rank R M = Module.rank R M₁ := ⟨fun ⟨h⟩ => LinearEquiv.rank_eq h, fun h => nonempty_linearEquiv_of_rank_eq h⟩ +@[deprecated (since := "2026-06-30")] +alias LinearEquiv.nonempty_equiv_iff_rank_eq := Module.nonempty_linearEquiv_iff_rank_eq + /-- Two finite and free modules are isomorphic if they have the same (finite) rank. -/ theorem FiniteDimensional.nonempty_linearEquiv_of_finrank_eq [Module.Finite R M] [Module.Finite R M'] (cond : finrank R M = finrank R M') : @@ -328,6 +334,18 @@ theorem basisUnique_repr_eq_zero_iff {ι : Type*} [Unique ι] (basisUnique ι h).repr.map_eq_zero_iff.mp (Finsupp.ext fun j => Subsingleton.elim i j ▸ hv), fun hv => by rw [hv, map_zero, Finsupp.zero_apply]⟩ +omit [StrongRankCondition R] in +theorem _root_.OrzechProperty.bijective_of_surjective_of_finrank_le + [OrzechProperty R] [Module.Finite R M] [Module.Finite R M'] + (f : M →ₗ[R] M') (hf : Function.Surjective f) (h : Module.finrank R M ≤ Module.finrank R M') : + Function.Bijective f := by + cases subsingleton_or_nontrivial R + -- TODO : figure out how to make `nontriviality` work here nicely + · have := Module.subsingleton R M + exact ⟨Function.injective_of_subsingleton f, hf⟩ + rcases finrank_le_iff_exists_linearMap.mp h with ⟨_, hi⟩ + exact OrzechProperty.bijective_of_surjective_of_injective _ _ hi hf + variable {R : Type*} [CommSemiring R] [StrongRankCondition R] {M : Type*} [AddCommMonoid M] [Module R M] [Module.Free R M] diff --git a/Mathlib/LinearAlgebra/Dimension/RankNullity.lean b/Mathlib/LinearAlgebra/Dimension/RankNullity.lean index 9f6f1ba0053b2f..1d820f2b0b100c 100644 --- a/Mathlib/LinearAlgebra/Dimension/RankNullity.lean +++ b/Mathlib/LinearAlgebra/Dimension/RankNullity.lean @@ -260,7 +260,7 @@ lemma Submodule.disjoint_ker_of_finrank_le [IsDomain R] [IsTorsionFree R M] {N : [AddCommGroup N] [Module R N] {L : Submodule R M} [Module.Finite R L] (f : M →ₗ[R] N) (h : finrank R L ≤ finrank R (L.map f)) : Disjoint L (LinearMap.ker f) := by - refine disjoint_iff.mpr <| LinearMap.injective_domRestrict_iff.mp <| LinearMap.ker_eq_bot.mp <| + refine LinearMap.injective_domRestrict_iff.mp <| LinearMap.ker_eq_bot.mp <| Submodule.rank_eq_zero.mp ?_ rw [← Submodule.finrank_eq_rank, Nat.cast_eq_zero] rw [← LinearMap.range_domRestrict] at h diff --git a/Mathlib/LinearAlgebra/Dual/Defs.lean b/Mathlib/LinearAlgebra/Dual/Defs.lean index 62a06d2c073886..0323991b305053 100644 --- a/Mathlib/LinearAlgebra/Dual/Defs.lean +++ b/Mathlib/LinearAlgebra/Dual/Defs.lean @@ -7,6 +7,7 @@ module public import Mathlib.LinearAlgebra.BilinearMap public import Mathlib.LinearAlgebra.Span.Defs +public import Mathlib.Tactic.CrossRefAttribute /-! # Dual vector spaces @@ -57,6 +58,7 @@ variable (R A M : Type*) variable [CommSemiring R] [AddCommMonoid M] [Module R M] /-- The left dual space of an R-module M is the R-module of linear maps `M → R`. -/ +@[wikidata Q752487] abbrev Dual (R M : Type*) [Semiring R] [AddCommMonoid M] [Module R M] := M →ₗ[R] R diff --git a/Mathlib/LinearAlgebra/FiniteDimensional/Basic.lean b/Mathlib/LinearAlgebra/FiniteDimensional/Basic.lean index ee2d03392f3e52..b948b99d513e9d 100644 --- a/Mathlib/LinearAlgebra/FiniteDimensional/Basic.lean +++ b/Mathlib/LinearAlgebra/FiniteDimensional/Basic.lean @@ -96,11 +96,11 @@ theorem _root_.Submodule.exists_linearEquiv_restrict_eq let eQ' := W'.prodEquivOfIsCompl Q' hQ' suffices Nonempty (Q ≃ₗ[K] Q') from ⟨eQ.symm ≪≫ₗ (LinearEquiv.prodCongr f this.some) ≪≫ₗ eQ', by aesop⟩ - refine LinearEquiv.nonempty_equiv_iff_rank_eq.mpr ?_ + refine Module.nonempty_linearEquiv_iff_rank_eq.mpr ?_ rw [← Cardinal.add_right_inj_of_lt_aleph0 (γ := Module.rank K W), - add_comm, ← rank_prod', LinearEquiv.nonempty_equiv_iff_rank_eq.mp ⟨eQ⟩, - add_comm, LinearEquiv.nonempty_equiv_iff_rank_eq.mp ⟨f⟩, - ← rank_prod', LinearEquiv.nonempty_equiv_iff_rank_eq.mp ⟨eQ'⟩] + add_comm, ← rank_prod', Module.nonempty_linearEquiv_iff_rank_eq.mp ⟨eQ⟩, + add_comm, Module.nonempty_linearEquiv_iff_rank_eq.mp ⟨f⟩, + ← rank_prod', Module.nonempty_linearEquiv_iff_rank_eq.mp ⟨eQ'⟩] exact Module.rank_lt_aleph0 K ↥W section @@ -383,7 +383,7 @@ theorem comap_eq_sup_ker_of_disjoint {p : Submodule K V} [FiniteDimensional K p] p.comap f = p ⊔ ker f := by refine le_antisymm (fun x hx ↦ ?_) (sup_le_iff.mpr ⟨h, ker_le_comap _⟩) obtain ⟨⟨y, hy⟩, hxy⟩ := - surjective_of_injective ((injective_restrict_iff_disjoint h).mpr h') ⟨f x, hx⟩ + surjective_of_injective ((injective_restrict_iff h).mpr h') ⟨f x, hx⟩ replace hxy : f y = f x := by simpa [Subtype.ext_iff] using hxy exact Submodule.mem_sup.mpr ⟨y, hy, x - y, by simp [hxy], add_sub_cancel y x⟩ @@ -666,3 +666,7 @@ theorem ker_pow_constant {f : End K V} {k : ℕ} end End end Module + +theorem AlgHom.bijective {K S : Type*} [Field K] [Ring S] [IsSimpleRing S] + [Algebra K S] [FiniteDimensional K S] (f : S →ₐ[K] S) : Function.Bijective f := + ⟨f.toRingHom.injective, f.toLinearMap.injective_iff_surjective.mp f.toRingHom.injective⟩ diff --git a/Mathlib/LinearAlgebra/FreeModule/Basic.lean b/Mathlib/LinearAlgebra/FreeModule/Basic.lean index 4e9d8b89e6b7bf..37d796cf745408 100644 --- a/Mathlib/LinearAlgebra/FreeModule/Basic.lean +++ b/Mathlib/LinearAlgebra/FreeModule/Basic.lean @@ -149,6 +149,7 @@ lemma iff_of_equiv {R R' M M'} [Semiring R] [AddCommMonoid M] [Module R M] instance shrink [Small.{w} M] : Module.Free R (Shrink.{w} M) := Module.Free.of_equiv (Shrink.linearEquiv R M).symm +set_option linter.dupNamespace false in @[deprecated (since := "2026-04-18")] alias Module.free_shrink := shrink variable (R M N) diff --git a/Mathlib/LinearAlgebra/FreeModule/StrongRankCondition.lean b/Mathlib/LinearAlgebra/FreeModule/StrongRankCondition.lean index 4e8dc56053307c..6d6e235b901027 100644 --- a/Mathlib/LinearAlgebra/FreeModule/StrongRankCondition.lean +++ b/Mathlib/LinearAlgebra/FreeModule/StrongRankCondition.lean @@ -12,10 +12,10 @@ public import Mathlib.LinearAlgebra.InvariantBasisNumber # Strong rank condition for commutative rings -We prove that any nontrivial commutative ring satisfies `StrongRankCondition`, meaning that -if there is an injective linear map `(Fin n → R) →ₗ[R] Fin m → R`, then `n ≤ m`. This implies that -any commutative ring satisfies `InvariantBasisNumber`: the rank of a finitely generated free -module is well defined. +We provide a shortcut instance for the fact that any nontrivial commutative ring satisfies +`StrongRankCondition`, meaning that if there is an injective linear map +`(Fin n → R) →ₗ[R] Fin m → R`, then `n ≤ m`. This implies that any commutative ring satisfies +`InvariantBasisNumber`: the rank of a finitely generated free module is well defined. ## Main result @@ -27,7 +27,6 @@ the `OrzechProperty`, that is, for any finitely generated `R`-module `M`, any surjective homomorphism `f : N → M` from a submodule `N` of `M` to `M` is injective. - ## References * [Orzech, Morris. *Onto endomorphisms are isomorphisms*][orzech1971] @@ -37,11 +36,7 @@ is injective. -/ -public section - - -variable (R : Type*) [CommRing R] [Nontrivial R] - -/-- Any nontrivial commutative ring satisfies the `StrongRankCondition`. -/ -instance (priority := 100) commRing_strongRankCondition : StrongRankCondition R := - inferInstance +/-- Shortcut instance for the fact that any nontrivial commutative ring satisfies +the strong rank condition. -/ +public instance (priority := 200) commRing_strongRankCondition + (R : Type*) [CommRing R] [Nontrivial R] : StrongRankCondition R := inferInstance diff --git a/Mathlib/LinearAlgebra/FreeProduct/Basic.lean b/Mathlib/LinearAlgebra/FreeProduct/Basic.lean index 48423cfb9ad65b..80a143e85978c8 100644 --- a/Mathlib/LinearAlgebra/FreeProduct/Basic.lean +++ b/Mathlib/LinearAlgebra/FreeProduct/Basic.lean @@ -7,6 +7,7 @@ module public import Mathlib.Algebra.DirectSum.Basic public import Mathlib.LinearAlgebra.TensorAlgebra.ToTensorPower +public import Mathlib.RingTheory.Congruence.Hom /-! # The free product of $R$-algebras @@ -65,43 +66,6 @@ theorem induction_lon {R : Type*} [Semiring R] {ι : Type*} [DecidableEq ι] end DirectSum -namespace RingQuot -universe uS uA uB - -open scoped Function -- required for scoped `on` notation - -/-- If two `R`-algebras are `R`-equivalent and their quotients by a relation `rel` are defined, -then their quotients are also `R`-equivalent. - -(Special case of the third isomorphism theorem.) -/ -def algEquivQuotAlgEquiv - {R : Type u} [CommSemiring R] {A B : Type v} [Semiring A] [Semiring B] - [Algebra R A] [Algebra R B] (f : A ≃ₐ[R] B) (rel : A → A → Prop) : - RingQuot rel ≃ₐ[R] RingQuot (rel on f.symm) := - AlgEquiv.ofAlgHom - (RingQuot.liftAlgHom R (s := rel) - ⟨AlgHom.comp (RingQuot.mkAlgHom R (rel on f.symm)) f, - fun x y h_rel ↦ by - apply RingQuot.mkAlgHom_rel - simpa [Function.onFun]⟩) - ((RingQuot.liftAlgHom R (s := rel on f.symm) - ⟨AlgHom.comp (RingQuot.mkAlgHom R rel) f.symm, - fun x y h ↦ by apply RingQuot.mkAlgHom_rel; simpa⟩)) - (by ext b; simp) (by ext a; simp) - -/-- If two (semi)rings are equivalent and their quotients by a relation `rel` are defined, -then their quotients are also equivalent. - -(Special case of `algEquiv_quot_algEquiv` when `R = ℕ`, which in turn is a special -case of the third isomorphism theorem.) -/ -def equivQuotEquiv {A B : Type v} [Semiring A] [Semiring B] (f : A ≃+* B) (rel : A → A → Prop) : - RingQuot rel ≃+* RingQuot (rel on f.symm) := - let f_alg : A ≃ₐ[ℕ] B := - AlgEquiv.ofRingEquiv (f := f) (fun n ↦ by simp) - algEquivQuotAlgEquiv f_alg rel |>.toRingEquiv - -end RingQuot - open TensorAlgebra DirectSum TensorPower variable {I : Type u} [DecidableEq I] {i : I} -- The type of the indexing set @@ -137,30 +101,41 @@ inductive rel : FreeTensorAlgebra R A → FreeTensorAlgebra R A → Prop (tprod R (⨁ i, A i) 2 (fun | 0 => lof R I A i a₁ | 1 => lof R I A i a₂)) (ι R <| lof R I A i (a₁ * a₂)) +/-- `rel` as a ring congruence. -/ +def ringCon : RingCon (FreeTensorAlgebra R A) := ringConGen (rel R A) + open scoped Function /-- The generating equivalence relation for elements of the power algebra that are identified in the free product -/ @[reducible, simp] def rel' := rel R A on ofDirectSum +/-- `rel'` as a ring congruence. -/ +def ringCon' : RingCon (PowerAlgebra R A) := ringConGen (rel' R A) + theorem rel_id (i : I) : rel R A (ι R <| lof R I A i 1) 1 := rel.id /-- The free product of the collection of `R`-algebras `A i`, as a quotient of `FreeTensorAlgebra R A` -/ -@[reducible] def _root_.LinearAlgebra.FreeProduct := RingQuot <| FreeProduct.rel R A +@[reducible] def _root_.LinearAlgebra.FreeProduct := FreeProduct.ringCon R A |>.Quotient /-- The free product of the collection of `R`-algebras `A i`, as a quotient of `PowerAlgebra R A` -/ -@[reducible] def asPowers := RingQuot <| FreeProduct.rel' R A +@[reducible] def asPowers := FreeProduct.ringCon' R A |>.Quotient /-- The `R`-algebra equivalence relating `FreeProduct` and `FreeProduct.asPowers`. -/ noncomputable def asPowersEquiv : asPowers R A ≃ₐ[R] FreeProduct R A := - RingQuot.algEquivQuotAlgEquiv - (powerAlgebraEquivFreeTensorAlgebra R A |>.symm) (FreeProduct.rel R A) + RingCon.congrₐ _ + (powerAlgebraEquivFreeTensorAlgebra R A |>.symm) (by + rw [ringCon', ringCon, rel'] + erw [RingCon.comap_ringConGen_ringEquiv] + congr + ext i x + simp [Function.onFun]) |>.symm -open RingQuot Function +open Function local infixr:60 " ∘ₐ " => AlgHom.comp @@ -170,27 +145,29 @@ instance instAlgebra : Algebra R (FreeProduct R A) := by infer_instance /-- The canonical quotient map `FreeTensorAlgebra R A →ₐ[R] FreeProduct R A`, as an `R`-algebra homomorphism -/ abbrev mkAlgHom : FreeTensorAlgebra R A →ₐ[R] FreeProduct R A := - RingQuot.mkAlgHom R (rel R A) + RingCon.mkₐ _ _ /-- The canonical linear map from the direct sum of the `A i` to the free product -/ -abbrev ι' : (⨁ i, A i) →ₗ[R] FreeProduct R A := +def ι' : (⨁ i, A i) →ₗ[R] FreeProduct R A := (mkAlgHom R A).toLinearMap ∘ₗ TensorAlgebra.ι R (M := ⨁ i, A i) @[simp] theorem ι_apply (x : ⨁ i, A i) : - ⟨Quot.mk (Rel <| rel R A) (TensorAlgebra.ι R x)⟩ = ι' R A x := by - aesop (add simp [ι', mkAlgHom, RingQuot.mkAlgHom, mkRingHom]) + ↑(TensorAlgebra.ι R x) = ι' R A x := by + aesop (add simp [ι', mkAlgHom]) /-- The injection into the free product of any `1 : A i` is the 1 of the free product. -/ theorem identify_one (i : I) : ι' R A (DirectSum.lof R I A i 1) = 1 := by - suffices ι' R A (DirectSum.lof R I A i 1) = mkAlgHom R A 1 by simpa - exact RingQuot.mkAlgHom_rel R <| rel_id R A (i := i) + suffices ι' R A (DirectSum.lof R I A i 1) = mkAlgHom R A 1 by simpa [← ι_apply] + exact Quotient.sound <| RingCon.le_ringConGen _ _ <| rel_id R A (i := i) /-- Multiplication in the free product of the injections of any two `aᵢ aᵢ': A i` for the same `i` is just the injection of multiplication `aᵢ * aᵢ'` in `A i`. -/ theorem mul_injections (a₁ a₂ : A i) : ι' R A (DirectSum.lof R I A i a₁) * ι' R A (DirectSum.lof R I A i a₂) = ι' R A (DirectSum.lof R I A i (a₁ * a₂)) := by - convert! RingQuot.mkAlgHom_rel R <| rel.prod + rw [← ι_apply, ← ι_apply, ← RingCon.coe_mul] + refine Quotient.sound <| RingCon.le_ringConGen _ _ <| ?_ + convert! rel.prod simp /-- The `i`th canonical injection, from `A i` to the free product, as @@ -210,34 +187,31 @@ irreducible_def ι (i : I) : A i →ₐ[R] FreeProduct R A := /-- The family of canonical injection maps, with `i` left implicit -/ irreducible_def of {i : I} : A i →ₐ[R] FreeProduct R A := ι R A i - /-- Universal property of the free product of algebras: for every `R`-algebra `B`, every family of maps `maps : (i : I) → (A i →ₐ[R] B)` lifts to a unique arrow `π` from `FreeProduct R A` such that `π ∘ ι i = maps i`. -/ @[simps] def lift : ({i : I} → A i →ₐ[R] B) ≃ (FreeProduct R A →ₐ[R] B) where toFun maps := - RingQuot.liftAlgHom R ⟨ - TensorAlgebra.lift R <| - DirectSum.toModule R I B <| - (@maps · |>.toLinearMap), - fun x y r ↦ by + RingCon.liftₐ _ + (TensorAlgebra.lift R <| DirectSum.toModule R I B <| (@maps · |>.toLinearMap)) + <| RingCon.ringConGen_le.2 fun x y r ↦ by cases r with | id => simp - | prod => simp⟩ + | prod => simp invFun π i := π ∘ₐ ι R A i left_inv π := by ext i aᵢ - aesop (add simp [ι, ι']) + simp [ι, ← ι_apply] right_inv maps := by ext i a - aesop (add simp [ι, ι']) + simp [ι, ← ι_apply] /-- Universal property of the free product of algebras, property: for every `R`-algebra `B`, every family of maps `maps : (i : I) → (A i →ₐ[R] B)` lifts to a unique arrow `π` from `FreeProduct R A` such that `π ∘ ι i = maps i`. -/ @[simp↓] theorem lift_comp_ι : lift R A maps ∘ₐ ι R A i = maps := by ext a - simp [lift_apply, ι] + simp [lift_apply, ι, ← ι_apply] @[simp↓] theorem lift_algebraMap (r : R) : lift R A maps (algebraMap R _ r) = algebraMap R _ r := by rw [lift_apply, AlgHom.commutes] @@ -246,6 +220,6 @@ to a unique arrow `π` from `FreeProduct R A` such that `π ∘ ι i = maps i`. (f : FreeProduct R A →ₐ[R] B) (h : ∀ i, f ∘ₐ ι R A i = maps) : f = lift R A maps := by ext i a; simp_rw [AlgHom.ext_iff] at h; specialize h i a - simp [h.symm, ι] + simp [h.symm, ι, ← ι_apply] end LinearAlgebra.FreeProduct diff --git a/Mathlib/LinearAlgebra/InvariantBasisNumber.lean b/Mathlib/LinearAlgebra/InvariantBasisNumber.lean index 4bb87559796efa..61512f8a35ca17 100644 --- a/Mathlib/LinearAlgebra/InvariantBasisNumber.lean +++ b/Mathlib/LinearAlgebra/InvariantBasisNumber.lean @@ -296,15 +296,20 @@ section attribute [local instance] Ideal.Quotient.field -/-- Nontrivial commutative rings have the invariant basis number property. - -There are two stronger results in mathlib: `commRing_strongRankCondition`, which says that any -nontrivial commutative ring satisfies the strong rank condition, and -`rankCondition_of_nontrivial_of_commSemiring`, which says that any nontrivial commutative semiring -satisfies the rank condition. - -We prove this instance separately to avoid dependency on -`Mathlib/LinearAlgebra/Charpoly/Basic.lean` or `Mathlib/LinearAlgebra/Matrix/ToLin.lean`. -/ +/-- +Nontrivial commutative rings satisfy the invariant basis number property. + +There are two stronger results in mathlib: +1. `CommRing.orzechProperty` in `Mathlib.RingTheory.FiniteType`, + which says that any commutative ring satisfies the Orzech property, and hence + (by `strongRankCondition_of_orzechProperty`) that nontrivial commutative rings satisfy + the strong rank condition. A shortcut instance `commRing_strongRankCondition` is also provided. +2. `rankCondition_of_nontrivial_of_commSemiring` in + `Mathlib.LinearAlgebra.Matrix.InvariantBasisNumber`, which says that + any nontrivial commutative semiring satisfies the rank condition. + +We prove this instance here anyway to reduce the required imports. +-/ instance (priority := 100) invariantBasisNumber_of_nontrivial_of_commRing {R : Type u} [CommRing R] [Nontrivial R] : InvariantBasisNumber R := ⟨fun e => diff --git a/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean b/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean index 4c5aef9b9bc3e3..ab631ec1d803f9 100644 --- a/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean +++ b/Mathlib/LinearAlgebra/LinearIndependent/Lemmas.lean @@ -523,6 +523,27 @@ theorem LinearIndependent.of_pairwise_dual_eq_zero_one (v : ι → M) (f : ι end Module +open Finsupp in +/-- A linearly independent family of vectors `f` remains linearly independent when we substitute one +of the terms with a vector `m` provided there exists a non-zero divisor `r`, such that `r • m` +belongs to the span of `f` with non-zero-divisor coefficients. -/ +lemma LinearIndependent.update [DecidableEq ι] [CommRing R] [AddCommGroup M] [Module R M] + {f : ι → M} (hf : LinearIndependent R f) (i : ι) (m : M) + (hg : ∃ r ∈ nonZeroDivisors R, ∃ l : ι →₀ R, + l i ∈ nonZeroDivisors R ∧ r • m = linearCombination R f l) : + LinearIndependent R (Function.update f i m) := by + rw [linearIndependent_iff] at hf ⊢ + obtain ⟨r, hr, l, hl, hg⟩ := hg + intros l' hl' + apply_fun (r • ·) at hl' + simp_rw [Pi.update_eq_sub_add_single, ← bilinearCombination_apply _ (S := R), map_add, map_sub, + bilinearCombination_apply, LinearMap.add_apply, LinearMap.sub_apply, + linearCombination_single_index, smul_add, smul_sub, smul_zero, smul_comm r (l' i) m, + hg, ← LinearMap.map_smul, smul_smul, ← linearCombination_single, ← map_sub, ← map_add] at hl' + replace hl' : ∀ j, (r * l' j - (single i (r * l' i)) j) + l' i * l j = 0 := + fun j ↦ DFunLike.congr_fun (hf _ hl') j + grind [mem_nonZeroDivisors_iff] + /-! ### Properties which require `DivisionRing K` diff --git a/Mathlib/LinearAlgebra/Matrix/Action.lean b/Mathlib/LinearAlgebra/Matrix/Action.lean index 0a432ad73fef8e..bf7e4f980e4f63 100644 --- a/Mathlib/LinearAlgebra/Matrix/Action.lean +++ b/Mathlib/LinearAlgebra/Matrix/Action.lean @@ -47,6 +47,9 @@ instance [DistribSMul S R] [SMulCommClass S R R] : SMulCommClass S (Matrix n n R instance [DistribSMul S R] [IsScalarTower S R R] : IsScalarTower S (Matrix n n R) (n → R) where smul_assoc := smul_mulVec +lemma ext_iff_smul {A B : Matrix n n R} : + A = B ↔ ∀ v : n → R, A • v = B • v := Matrix.ext_iff_mulVec + end mulVec /-! ## `*ᵥ` as a right-module -/ diff --git a/Mathlib/LinearAlgebra/Matrix/Defs.lean b/Mathlib/LinearAlgebra/Matrix/Defs.lean index 73ad6a744b33d9..e36bc8ec9d5b62 100644 --- a/Mathlib/LinearAlgebra/Matrix/Defs.lean +++ b/Mathlib/LinearAlgebra/Matrix/Defs.lean @@ -135,7 +135,16 @@ theorem map_injective {f : α → β} (hf : Function.Injective f) : theorem map_involutive {f : α → α} (hf : Function.Involutive f) : Function.Involutive fun M : Matrix m n α ↦ M.map f := by intro; simp [hf] -/-- The transpose of a matrix. -/ +/-- The transpose of a matrix. + +This is available in bundled forms as: +* `Matrix.transposeAddEquiv` +* `Matrix.transposeLinearEquiv` +* `Matrix.transposeRingEquiv` +* `Matrix.transposeAlgEquiv` +* `RingEquiv.mopMatrix` +* `AlgEquiv.mopMatrix` +-/ def transpose (M : Matrix m n α) : Matrix n m α := of fun x y => M y x @@ -153,6 +162,9 @@ instance inhabited [Inhabited α] : Inhabited (Matrix m n α) := instance add [Add α] : Add (Matrix m n α) := inferInstanceAs <| Add (m → n → α) +instance smul [SMul R α] : SMul R (Matrix m n α) where + smul a b := fun i ↦ a • b i + instance addSemigroup [AddSemigroup α] : AddSemigroup (Matrix m n α) := inferInstanceAs <| AddSemigroup (m → n → α) @@ -165,9 +177,8 @@ instance zero [Zero α] : Zero (Matrix m n α) := instance addZeroClass [AddZeroClass α] : AddZeroClass (Matrix m n α) := inferInstanceAs <| AddZeroClass (m → n → α) -instance addMonoid [AddMonoid α] : AddMonoid (Matrix m n α) where - __ : AddMonoid (Matrix m n α) := inferInstanceAs <| AddMonoid (m → n → α) - nsmul a b := fun i ↦ a • b i +instance addMonoid [AddMonoid α] : AddMonoid (Matrix m n α) := + inferInstanceAs <| AddMonoid (m → n → α) instance addCommMonoid [AddCommMonoid α] : AddCommMonoid (Matrix m n α) := inferInstanceAs <| AddCommMonoid (m → n → α) @@ -181,9 +192,8 @@ instance involutiveNeg [InvolutiveNeg α] : InvolutiveNeg (Matrix m n α) := instance sub [Sub α] : Sub (Matrix m n α) := inferInstanceAs <| Sub (m → n → α) -instance addGroup [AddGroup α] : AddGroup (Matrix m n α) where - __ : AddGroup (Matrix m n α) := inferInstanceAs <| AddGroup (m → n → α) - zsmul a b := fun i ↦ a • b i +instance addGroup [AddGroup α] : AddGroup (Matrix m n α) := + inferInstanceAs <| AddGroup (m → n → α) instance addCommGroup [AddCommGroup α] : AddCommGroup (Matrix m n α) := inferInstanceAs <| AddCommGroup (m → n → α) @@ -197,9 +207,6 @@ instance subsingleton [Subsingleton α] : Subsingleton (Matrix m n α) := instance nonempty [Nonempty m] [Nonempty n] [Nontrivial α] : Nontrivial (Matrix m n α) := Function.nontrivial -instance smul [SMul R α] : SMul R (Matrix m n α) where - smul a b := fun i ↦ a • b i - instance smulCommClass [SMul R α] [SMul S α] [SMulCommClass R S α] : SMulCommClass R S (Matrix m n α) := Pi.smulCommClass @@ -260,6 +267,9 @@ section @[simp] theorem zero_apply [Zero α] (i : m) (j : n) : (0 : Matrix m n α) i j = 0 := rfl +@[simp] +theorem of_symm_zero [Zero α] : of.symm (0 : Matrix m n α) = (0 : m → n → α) := rfl + @[simp] theorem add_apply [Add α] (A B : Matrix m n α) (i : m) (j : n) : (A + B) i j = (A i j) + (B i j) := rfl diff --git a/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean b/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean index 87b6da663393c1..40eface05904fd 100644 --- a/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean +++ b/Mathlib/LinearAlgebra/Matrix/Determinant/Basic.lean @@ -85,7 +85,7 @@ theorem det_diagonal {d : n → R} : det (diagonal d) = ∏ i, d i := by · simp · simp -theorem det_zero (_ : Nonempty n) : det (0 : Matrix n n R) = 0 := +@[simp] theorem det_zero [Nonempty n] : det (0 : Matrix n n R) = 0 := (detRowAlternating : (n → R) [⋀^n]→ₗ[R] R).map_zero @[simp] diff --git a/Mathlib/LinearAlgebra/Matrix/Determinant/Bird.lean b/Mathlib/LinearAlgebra/Matrix/Determinant/Bird.lean new file mode 100644 index 00000000000000..f482861da89563 --- /dev/null +++ b/Mathlib/LinearAlgebra/Matrix/Determinant/Bird.lean @@ -0,0 +1,131 @@ +/- +Copyright (c) 2026 Paul Cadman. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Paul Cadman +-/ +module + +public import Mathlib.Algebra.Ring.Defs + +/-! + +# A division-free determinant algorithm + +This file defines `birdDet`, an implementation of an division-free algorithm for +computing determinants. The algorithm runs in O(n^4) for an n-by-n matrix. + +This determinant algorithm comes from: + +Title: A simple division-free algorithm for computing determinants. +Author: Richard S. Bird +URL: https://doi.org/10.1016/j.ipl.2011.08.006 + +## Main definitions + +- `BirdDet.birdDet`: The entrypoint for the determinant calculation. +- `BirdDet.get`: matrix entry lookup. +- `BirdDet.sumFrom`: The sum `f lo + ... + f (n - 1)`. +- `BirdDet.iter`: The internal scalar recurrence for Bird's algorithm. + +## Main lemmas + +The lemmas in this file are unfolding equations. + +-/ + +public section + +namespace BirdDet + +variable {R : Type*} [CommRing R] + +/-- +`get n A i j` returns the (i, j)th entry of the `n × n` matrix whose entries are +stored in `A` in row-major order. + +The function does not check the matrix index bounds. +-/ +@[expose] protected def get (n : ℕ) (A : Array R) (i j : ℕ) : R := + A.getD (i * n + j) 0 + +/-- Sum `f lo + ... + f (n - 1)`. Returns zero when `n <= lo`. -/ +@[expose] protected def sumFrom (n lo : ℕ) (f : ℕ → R) : R := + if lo < n then f lo + BirdDet.sumFrom n (lo + 1) f else 0 + +/-- +# Scalar formula for one recurrence step. + +Bird's paper defines a matrix recursion for an `n × n` matrix `A`: + +``` +F_0 = A +F_{t+1} = μ(F_t) * A +``` + +where `μ(F_t)` is obtained from `F_t` by replacing each diagonal entry +`F_t k k` with the negative sum of the diagonal entries below it, setting the +entries in the lower triangular part to 0, and leaving all other entries +unchanged: + +``` +μ(F_t) = + 0 if i >= j + - ∑ k from i+1 to n-1, F_t k k if i = j + F_t i j if i < j +``` + +If we write out the entry-wise matrix multiplication `F_{t+1} i j = (μ(F_t) * A) i j` +we obtain: + +``` +F_{t+1} i j = + - (∑ k from i+1 to n-1, F_t k k) * (A i j) + + ∑ k from i+1 to n-1, (F_t i k) * (A k j) +``` +-/ +@[expose] protected def iter (n : ℕ) (A : Array R) (t : ℕ) (F : ℕ → ℕ → R) : ℕ → ℕ → R := + match t with + | 0 => F + | t + 1 => fun i j => + -(BirdDet.sumFrom n (i + 1) fun k => BirdDet.iter n A t F k k) * BirdDet.get n A i j + + BirdDet.sumFrom n (i + 1) fun k => BirdDet.iter n A t F i k * BirdDet.get n A k j + +/-- +`birdDet n A` computes the determinant of the `n × n` matrix whose entries are +stored in `A` in row-major order. +-/ +@[expose] def birdDet (n : ℕ) (A : Array R) : R := + match n with + | 0 => 1 + | k + 1 => (-1 : R) ^ k * BirdDet.iter n A k (BirdDet.get n A) 0 0 + +/- Unfolding lemmas -/ + +theorem sumFrom_step (n lo : ℕ) (f : ℕ → R) (h : lo < n) : + BirdDet.sumFrom n lo f = f lo + BirdDet.sumFrom n (lo + 1) f := by + rw [BirdDet.sumFrom] + simp [h] + +theorem sumFrom_stop (n lo : ℕ) (f : ℕ → R) (h : ¬ lo < n) : + BirdDet.sumFrom n lo f = 0 := by + rw [BirdDet.sumFrom] + simp [h] + +theorem iter_zero (n : ℕ) (A : Array R) (F : ℕ → ℕ → R) (i j : ℕ) : + BirdDet.iter n A 0 F i j = F i j := rfl + +theorem iter_succ (n : ℕ) (A : Array R) (t : ℕ) (F : ℕ → ℕ → R) (i j : ℕ) : + BirdDet.iter n A (t + 1) F i j = + -(BirdDet.sumFrom n (i + 1) fun k => BirdDet.iter n A t F k k) * BirdDet.get n A i j + + BirdDet.sumFrom n (i + 1) fun k => BirdDet.iter n A t F i k * BirdDet.get n A k j := rfl + +theorem birdDet_zero (A : Array R) : birdDet 0 A = 1 := rfl + +theorem birdDet_eq (n k : ℕ) (A : Array R) (hn : n = k + 1) : + birdDet n A = (-1 : R) ^ k * BirdDet.iter n A k (BirdDet.get n A) 0 0 := by + subst hn + rfl + +end BirdDet + +end diff --git a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean index 0b8ea843931ff7..85a3ed415fca23 100644 --- a/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean +++ b/Mathlib/LinearAlgebra/Matrix/GeneralLinearGroup/FinTwo.lean @@ -274,7 +274,7 @@ lemma IsParabolic.pow {g : GL (Fin 2) K} (hg : IsParabolic g) [CharZero K] refine fun ha ↦ (g ^ 2).det_ne_zero ?_ rw [ha, map_zero, zero_add] at hg rw [← hg] at hmsq - rw [Units.val_pow_eq_pow_val, hmsq, det_zero ⟨0⟩] + rw [Units.val_pow_eq_pow_val, hmsq, det_zero] lemma isParabolic_iff_of_upperTriangular {g : GL (Fin 2) K} (hg : g 1 0 = 0) : g.IsParabolic ↔ g 0 0 = g 1 1 ∧ g 0 1 ≠ 0 := diff --git a/Mathlib/LinearAlgebra/Matrix/Ideal.lean b/Mathlib/LinearAlgebra/Matrix/Ideal.lean index c3bc08418c5df5..1ed4a0c51d8756 100644 --- a/Mathlib/LinearAlgebra/Matrix/Ideal.lean +++ b/Mathlib/LinearAlgebra/Matrix/Ideal.lean @@ -261,7 +261,7 @@ theorem matrix_monotone : Monotone (matrix (R := R) n) := theorem matrix_strictMono_of_nonempty [h : Nonempty n] : StrictMono (matrix (R := R) n) := matrix_monotone n |>.strictMono_of_injective <| - .comp (fun _ _ => mk.inj) <| (RingCon.matrix_injective n).comp ringCon_injective + .comp (fun _ _ => ofRingCon.inj) <| (RingCon.matrix_injective n).comp ringCon_injective @[simp] theorem matrix_bot : (⊥ : TwoSidedIdeal R).matrix n = ⊥ := diff --git a/Mathlib/LinearAlgebra/Matrix/Nondegenerate.lean b/Mathlib/LinearAlgebra/Matrix/Nondegenerate.lean index b83592c1b37c2a..3a8f262c4f5e2f 100644 --- a/Mathlib/LinearAlgebra/Matrix/Nondegenerate.lean +++ b/Mathlib/LinearAlgebra/Matrix/Nondegenerate.lean @@ -26,7 +26,7 @@ namespace Matrix section Finite -variable {m n R A : Type*} [CommRing R] [Finite m] [Finite n] (M : Matrix m n R) +variable {m n R A : Type*} [NonUnitalNonAssocSemiring R] [Finite m] [Finite n] (M : Matrix m n R) attribute [local instance] Fintype.ofFinite @@ -39,28 +39,86 @@ def SeparatingLeft : Prop := (∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) /-- A matrix `M` is nondegenerate if it is both left-separating and right-separating. -/ +@[mk_iff] structure Nondegenerate (M : Matrix m n R) : Prop where separatingLeft : SeparatingLeft M separatingRight : SeparatingRight M end Finite -variable {m n R A : Type*} [CommRing R] [Fintype m] [Fintype n] [CommRing A] [IsDomain A] - {M : Matrix m n R} +section CommSemiring -lemma separatingRight_def : M.SeparatingRight ↔ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by +variable {m n R : Type*} [CommSemiring R] {M : Matrix m n R} + +lemma separatingRight_def [Fintype m] [Fintype n] : + M.SeparatingRight ↔ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by refine forall_congr' fun w ↦ ⟨fun hM hw ↦ hM ?_, fun hM hw ↦ hM ?_⟩ <;> convert! hw -lemma separatingLeft_def : M.SeparatingLeft ↔ (∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) := by +lemma separatingLeft_def [Fintype m] [Fintype n] : + M.SeparatingLeft ↔ (∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) := by refine forall_congr' fun v ↦ ⟨fun hM hv ↦ hM ?_, fun hM hv ↦ hM ?_⟩ <;> convert! hv -lemma nondegenerate_def : M.Nondegenerate ↔ - (∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) ∧ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by - constructor - · exact fun h ↦ ⟨separatingLeft_def.mp h.1, separatingRight_def.mp h.2⟩ - · exact fun h ↦ ⟨separatingLeft_def.mpr h.1, separatingRight_def.mpr h.2⟩ +lemma nondegenerate_def [Fintype m] [Fintype n] : + M.Nondegenerate ↔ + (∀ v, (∀ w, v ⬝ᵥ M *ᵥ w = 0) → v = 0) ∧ (∀ w, (∀ v, v ⬝ᵥ M *ᵥ w = 0) → w = 0) := by + rw [nondegenerate_iff, separatingLeft_def, separatingRight_def] + +theorem separatingLeft_iff_forall_vecMul_eq_zero [Fintype m] [Finite n] : + M.SeparatingLeft ↔ ∀ v, v ᵥ* M = 0 → v = 0 := by + have := Fintype.ofFinite n + rw [separatingLeft_def] + refine ⟨fun h v hv ↦ h v fun w ↦ ?_, fun h w hw ↦ h w <| funext fun i ↦ ?_⟩ + · simp [dotProduct_mulVec, hv] + · classical simpa using! hw <| Pi.single i 1 + +theorem separatingRight_iff_forall_mulVec_eq_zero [Finite m] [Fintype n] : + M.SeparatingRight ↔ ∀ v, M *ᵥ v = 0 → v = 0 := by + have := Fintype.ofFinite m + rw [separatingRight_def] + refine ⟨fun h v hv ↦ h v fun w ↦ ?_, fun h w hw ↦ h w <| funext fun i ↦ ?_⟩ + · simp [hv] + · classical simpa using hw <| Pi.single i 1 + +theorem SeparatingLeft.eq_zero_of_vecMul_eq_zero [Fintype m] [Finite n] (hM : M.SeparatingLeft) + {v : m → R} (hv : v ᵥ* M = 0) : v = 0 := + separatingLeft_iff_forall_vecMul_eq_zero.mp hM v hv + +theorem SeparatingRight.eq_zero_of_mulVec_eq_zero [Finite m] [Fintype n] (hM : M.SeparatingRight) + {v : n → R} (hv : M *ᵥ v = 0) : v = 0 := + separatingRight_iff_forall_mulVec_eq_zero.mp hM v hv + +theorem nondegenerate_iff_forall_vecMul_and_mulVec_eq_zero [Fintype m] [Fintype n] : + M.Nondegenerate ↔ (∀ v, v ᵥ* M = 0 → v = 0) ∧ (∀ v, M *ᵥ v = 0 → v = 0) := by + rw [nondegenerate_iff, separatingLeft_iff_forall_vecMul_eq_zero, + separatingRight_iff_forall_mulVec_eq_zero] + +@[simp] +theorem separatingLeft_transpose_iff [Finite m] [Finite n] : + Mᵀ.SeparatingLeft ↔ M.SeparatingRight := by + have := Fintype.ofFinite m + have := Fintype.ofFinite n + simp_rw [separatingLeft_def, separatingRight_def, dotProduct_transpose_mulVec] + +alias ⟨_, SeparatingRight.separatingLeft_transpose⟩ := separatingLeft_transpose_iff + +@[simp] +theorem separatingRight_transpose_iff [Finite m] [Finite n] : + Mᵀ.SeparatingRight ↔ M.SeparatingLeft := by + have := Fintype.ofFinite m + have := Fintype.ofFinite n + simp_rw [separatingRight_def, separatingLeft_def, dotProduct_transpose_mulVec] + +alias ⟨_, SeparatingLeft.separatingRight_transpose⟩ := separatingRight_transpose_iff + +@[simp] +theorem nondegenerate_transpose_iff [Finite m] [Finite n] : Mᵀ.Nondegenerate ↔ M.Nondegenerate := by + simp [nondegenerate_iff, and_comm] + +alias ⟨_, Nondegenerate.transpose⟩ := nondegenerate_transpose_iff + +variable [Fintype m] [Fintype n] /-- If `M` is nondegenerate and `w * M * v = 0` for all `w`, then `v = 0`. -/ theorem Nondegenerate.eq_zero_of_ortho (hM : Nondegenerate M) {v : m → R} @@ -73,42 +131,53 @@ theorem Nondegenerate.exists_not_ortho_of_ne_zero (hM : Nondegenerate M) not_forall.mp (mt hM.eq_zero_of_ortho hv) /-- If `M` is nondegenerate and `w * M * v = 0` for all `v`, then `w = 0`. -/ -theorem Nondegenerate.eq_zero_of_ortho' {M : Matrix m n R} (hM : Nondegenerate M) {w : n → R} +theorem Nondegenerate.eq_zero_of_ortho' (hM : Nondegenerate M) {w : n → R} (hw : ∀ v, v ⬝ᵥ M *ᵥ w = 0) : w = 0 := (nondegenerate_def.mp hM).2 w hw /-- If `M` is nondegenerate and `w ≠ 0`, then there is some `v` such that `v * M * w ≠ 0`. -/ -theorem Nondegenerate.exists_not_ortho_of_ne_zero' {M : Matrix m n R} (hM : Nondegenerate M) - {w : n → R} (hw : w ≠ 0) : ∃ v, v ⬝ᵥ M *ᵥ w ≠ 0 := +theorem Nondegenerate.exists_not_ortho_of_ne_zero' (hM : Nondegenerate M) {w : n → R} (hw : w ≠ 0) : + ∃ v, v ⬝ᵥ M *ᵥ w ≠ 0 := not_forall.mp (mt hM.eq_zero_of_ortho' hw) +end CommSemiring + section Determinant -variable [DecidableEq m] {M : Matrix m m A} +variable {m R : Type*} [CommRing R] [Fintype m] [DecidableEq m] {M : Matrix m m R} + +open scoped nonZeroDivisors + +private theorem SeparatingLeft.of_det_mem_nonZeroDivisors (hM : M.det ∈ R⁰) : M.SeparatingLeft := by + refine separatingLeft_def.mpr fun v h ↦ funext fun i ↦ mem_nonZeroDivisors_iff_left.mp hM _ ?_ + simpa using h <| M.cramer <| Pi.single i 1 -/-- If `M` is square and has nonzero determinant, then `M` as a bilinear form on `n → A` is +theorem Nondegenerate.of_det_mem_nonZeroDivisors (hM : M.det ∈ R⁰) : M.Nondegenerate where + separatingLeft := .of_det_mem_nonZeroDivisors hM + separatingRight := separatingLeft_transpose_iff.mp <| .of_det_mem_nonZeroDivisors <| by simpa + +/-- If `M` is square and has nonzero determinant, then `M` as a bilinear form on `n → R` is nondegenerate. The "iff" implication, `nondegenerate_iff_det_ne_zero`, is proved in a later file. See also `BilinForm.nondegenerateOfDetNeZero'` and `BilinForm.nondegenerateOfDetNeZero`. -/ -theorem nondegenerate_of_det_ne_zero (hM : M.det ≠ 0) : Nondegenerate M := by - refine nondegenerate_def.mpr ⟨fun v h ↦ ?_, fun w h ↦ ?_⟩ - · ext i - specialize h (M.cramer (Pi.single i 1)) - simp_all - · ext i - contrapose! h - use Pi.single i 1 ᵥ* M.adjugate - rw [dotProduct_mulVec, vecMul_vecMul, adjugate_mul] - simp_all [dotProduct, smul_apply, smul_eq_mul, Matrix.one_apply] - -theorem eq_zero_of_vecMul_eq_zero (hM : M.det ≠ 0) {v : m → A} +theorem nondegenerate_of_det_ne_zero [NoZeroDivisors R] (hM : M.det ≠ 0) : M.Nondegenerate := + .of_det_mem_nonZeroDivisors <| mem_nonZeroDivisors_of_ne_zero hM + +theorem eq_zero_of_det_mem_nonZeroDivisors_of_vecMul_eq_zero (hM : M.det ∈ R⁰) + {v : m → R} (hv : v ᵥ* M = 0) : v = 0 := + Nondegenerate.of_det_mem_nonZeroDivisors hM |>.separatingLeft.eq_zero_of_vecMul_eq_zero hv + +theorem eq_zero_of_vecMul_eq_zero [NoZeroDivisors R] (hM : M.det ≠ 0) {v : m → R} (hv : v ᵥ* M = 0) : v = 0 := - (nondegenerate_of_det_ne_zero hM).eq_zero_of_ortho fun w => by - rw [dotProduct_mulVec, hv, zero_dotProduct] + nondegenerate_of_det_ne_zero hM |>.separatingLeft.eq_zero_of_vecMul_eq_zero hv + +theorem eq_zero_of_det_mem_nonZeroDivisors_of_mulVec_eq_zero (hM : M.det ∈ R⁰) + {v : m → R} (hv : M *ᵥ v = 0) : v = 0 := + Nondegenerate.of_det_mem_nonZeroDivisors hM |>.separatingRight.eq_zero_of_mulVec_eq_zero hv -theorem eq_zero_of_mulVec_eq_zero (hM : M.det ≠ 0) {v : m → A} +theorem eq_zero_of_mulVec_eq_zero [NoZeroDivisors R] (hM : M.det ≠ 0) {v : m → R} (hv : M *ᵥ v = 0) : v = 0 := - eq_zero_of_vecMul_eq_zero (by rwa [det_transpose]) ((vecMul_transpose M v).trans hv) + nondegenerate_of_det_ne_zero hM |>.separatingRight.eq_zero_of_mulVec_eq_zero hv end Determinant diff --git a/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean b/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean index 21703a4f35bf9d..0cda6cc66b8de5 100644 --- a/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean +++ b/Mathlib/LinearAlgebra/Matrix/NonsingularInverse.lean @@ -416,7 +416,7 @@ theorem det_nonsing_inv : A⁻¹.det = A.det⁻¹ʳ := by rw [Ring.inverse_invertible, ← invOf_eq_nonsing_inv, det_invOf] cases isEmpty_or_nonempty n · rw [det_isEmpty, det_isEmpty, Ring.inverse_one] - · rw [Ring.inverse_non_unit _ h, nonsing_inv_apply_not_isUnit _ h, det_zero ‹_›] + · rw [Ring.inverse_non_unit _ h, nonsing_inv_apply_not_isUnit _ h, det_zero] theorem isUnit_nonsing_inv_det (h : IsUnit A.det) : IsUnit A⁻¹.det := .of_mul_eq_one _ (A.det_nonsing_inv_mul_det h) diff --git a/Mathlib/LinearAlgebra/Matrix/Rank.lean b/Mathlib/LinearAlgebra/Matrix/Rank.lean index bd582b3ec5338e..d2e68d01aad5b4 100644 --- a/Mathlib/LinearAlgebra/Matrix/Rank.lean +++ b/Mathlib/LinearAlgebra/Matrix/Rank.lean @@ -44,7 +44,7 @@ section Infinite variable [Semiring R] /-- The rank of a matrix, defined as the dimension of its column space, as a cardinal. -/ -noncomputable def cRank (A : Matrix m n R) : Cardinal := Module.rank R <| span R <| range Aᵀ +noncomputable def cRank (A : Matrix m n R) : Cardinal := Module.rank R <| span R <| range A.col @[simp] theorem cRank_subsingleton [Subsingleton R] (A : Matrix m n R) : A.cRank = 1 := @@ -59,8 +59,8 @@ lemma lift_cRank_submatrix_le (A : Matrix m n R) (r : m₀ → m) (c : n₀ → Submodule.rank_mono <| span_mono <| by rintro _ ⟨x, rfl⟩; exact ⟨c x, rfl⟩ refine (Cardinal.lift_monotone h).trans ?_ let f : (m → R) →ₗ[R] (m₀ → R) := LinearMap.funLeft R R r - have h_eq : Submodule.map f (span R (range Aᵀ)) = span R (range (A.submatrix r id)ᵀ) := by - rw [LinearMap.map_span, ← image_univ, image_image, transpose_submatrix] + have h_eq : Submodule.map f (span R (range A.col)) = span R (range (A.submatrix r id).col) := by + simp_rw [LinearMap.map_span, ← image_univ, image_image, col_eq_transpose, transpose_submatrix] aesop rw [cRank, ← h_eq] have hwin := lift_rank_map_le f (span R (range Aᵀ)) @@ -78,7 +78,8 @@ lemma cRank_le_card_height [StrongRankCondition R] [Fintype m] (A : Matrix m n R lemma cRank_le_card_width [StrongRankCondition R] [Fintype n] (A : Matrix m n R) : A.cRank ≤ Fintype.card n := - (rank_span_le ..).trans <| by simpa using Cardinal.mk_range_le_lift (f := Aᵀ) + (rank_span_le ..).trans <| + by simpa [col_eq_transpose] using Cardinal.mk_range_le_lift (f := A.col) /-- The rank of a matrix, defined as the dimension of its column space, as a term in `ℕ∞`. -/ noncomputable def eRank (A : Matrix m n R) : ℕ∞ := A.cRank.toENat @@ -125,9 +126,9 @@ theorem rank_subsingleton [CommSemiring R] [Subsingleton R] (A : Matrix m n R) : @[simp] theorem cRank_one [Semiring R] [Nontrivial R] [DecidableEq m] [StrongRankCondition R] : (cRank (1 : Matrix m m R)) = lift.{uR} #m := by - have h : LinearIndependent R (1 : Matrix m m R)ᵀ := by + have h : LinearIndependent R (1 : Matrix m m R).col := by convert! Pi.linearIndependent_single_one m R - simp [funext_iff, Matrix.one_eq_pi_single] + simp [funext_iff, one_apply, Pi.single_apply] rw [cRank, rank_span h, ← lift_umax, ← Cardinal.mk_range_eq_of_injective h.injective, lift_id'] @[simp] theorem eRank_one [Semiring R] [Nontrivial R] [DecidableEq m] [StrongRankCondition R] : @@ -143,12 +144,12 @@ theorem rank_one [CommSemiring R] [DecidableEq n] [StrongRankCondition R] : theorem rank_zero [CommSemiring R] [Nontrivial R] : rank (0 : Matrix m n R) = 0 := by rw [rank, mulVecLin_zero, LinearMap.range_zero, finrank_bot] -set_option backward.isDefEq.respectTransparency false in @[simp] theorem cRank_zero {m n : Type*} [Semiring R] [Nontrivial R] : cRank (0 : Matrix m n R) = 0 := by obtain hn | hn := isEmpty_or_nonempty n · rw [cRank, range_eq_empty, span_empty, rank_bot] - rw [cRank, transpose_zero, range_zero, span_zero_singleton, rank_bot] + rw [cRank, col_eq_transpose, transpose_zero, of_symm_zero, range_zero, span_zero_singleton, + rank_bot] @[simp] theorem eRank_zero {m n : Type*} [Semiring R] [Nontrivial R] : eRank (0 : Matrix m n R) = 0 := by @@ -337,9 +338,9 @@ theorem cRank_diagonal [DecidableEq m] (w : m → R) : convert! hli'.comp Subtype.val Subtype.val_injective ext ⟨j, hj⟩ k simp [w', diagonal, hj, Pi.single_apply, eq_comm] - have hrw : insert 0 (range (diagonal w)ᵀ) = insert 0 (range w') := by + have hrw : insert 0 (range (diagonal w).col) = insert 0 (range w') := by suffices ∀ a, diagonal w a = 0 ∨ ∃ b, w b ≠ 0 ∧ diagonal w b = diagonal w a - by simpa [subset_antisymm_iff, subset_def, w'] + by aesop (add simp [col_eq_transpose, subset_def]) simp_rw [or_iff_not_imp_right, not_exists, not_and, not_imp_not] simp +contextual [funext_iff, diagonal] rw [cRank, ← span_insert_zero, hrw, span_insert_zero, rank_span h, diff --git a/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean b/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean index c1d4b8090487ed..1ba86b265e8534 100644 --- a/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean +++ b/Mathlib/LinearAlgebra/Matrix/SpecialLinearGroup.lean @@ -489,22 +489,38 @@ variable {ι F : Type*} [DecidableEq ι] [Fintype ι] [CommRing F] /-- The transvection `1 + b · E_{i,j}` (the identity plus `b` in position `(i, j)`) as an element of `SL ι F`, when `i ≠ j`. -/ -noncomputable def SpecialLinearGroup.transvection {i j : ι} (hij : i ≠ j) (b : F) : +def SpecialLinearGroup.transvection {i j : ι} (hij : i ≠ j) (b : F) : Matrix.SpecialLinearGroup ι F := - ⟨(1 : Matrix ι ι F) + single i j b, Matrix.det_transvection_of_ne i j hij b⟩ + ⟨Matrix.transvection i j b, Matrix.det_transvection_of_ne i j hij b⟩ namespace SpecialLinearGroup lemma transvection_coe {i j : ι} (hij : i ≠ j) (b : F) : (transvection hij b) = (1 : Matrix ι ι F) + single i j b := rfl +@[simp] +lemma transvection_coeff_zero {i j : ι} (hij : i ≠ j) : + transvection hij (0 : F) = 1 := by ext; simp [transvection_coe] + /-- The transvection `transvection i j hij b` acts on `e_i = Pi.single i 1` as the identity. -/ +lemma transvection_smul_single_fst {i j : ι} (hij : i ≠ j) (b : F) : + (transvection hij b) • (Pi.single i 1 : ι → F) = Pi.single i 1 := by + simp [SpecialLinearGroup.smul_def, -mulVec_single, transvection_coe, + add_mulVec, single_mulVec_eq, hij] + +@[deprecated transvection_smul_single_fst (since := "2026-06-22")] lemma transvection_mulVec_single_self {i j : ι} (hij : i ≠ j) (b : F) : (transvection hij b).1 *ᵥ (Pi.single i (1 : F)) = Pi.single i 1 := by rw [transvection_coe] simp [-mulVec_single, add_mulVec, single_mulVec_eq, hij] /-- The transvection `transvection i j hij b` acts on `e_j = Pi.single j 1` by adding `b·e_i`. -/ +lemma transvection_smul_single_snd {i j : ι} (hij : i ≠ j) (b : F) : + (transvection hij b) • (Pi.single j 1 : ι → F) = Pi.single j 1 + b • Pi.single i 1 := by + simp [SpecialLinearGroup.smul_def, transvection_coe, -mulVec_single, + add_mulVec, single_mulVec_eq] + +@[deprecated transvection_smul_single_snd (since := "2026-06-22")] lemma transvection_mulVec_single_other {i j : ι} (hij : i ≠ j) (b : F) : (transvection hij b).1 *ᵥ (Pi.single j (1 : F)) = Pi.single j 1 + b • Pi.single i 1 := by rw [transvection_coe] @@ -512,8 +528,7 @@ lemma transvection_mulVec_single_other {i j : ι} (hij : i ≠ j) (b : F) : /-- Inverse of a transvection: `transvection i j hij b * transvection i j hij (-b) = 1`. -/ lemma transvection_mul_neg {i j : ι} (hij : i ≠ j) (b : F) : - transvection hij b * transvection hij (-b) = 1 := by - ext : 1 + transvection hij b * transvection hij (-b) = 1 := Subtype.ext <| by simp [transvection_coe, mul_add, add_mul, single_mul_single_of_ne _ _ _ _ hij.symm, ← single_neg] @@ -537,6 +552,29 @@ lemma transvection_mem_center_iff {i j : ι} (hij : i ≠ j) (b : F) : end SpecialLinearGroup +namespace TransvectionStruct + +variable {n R : Type*} [Fintype n] [DecidableEq n] [CommRing R] + +/-- Any transvection structure can be converted to a special linear matrix. -/ +def toSpecialLinearGroup (t : TransvectionStruct ι F) : + SpecialLinearGroup ι F := + SpecialLinearGroup.transvection t.hij t.c + +lemma toSpecialLinearGroup_def (t : TransvectionStruct ι F) : + t.toSpecialLinearGroup = SpecialLinearGroup.transvection t.hij t.c := rfl + +@[simp] +lemma toSpecialLinearGroup_coe (t : TransvectionStruct ι F) : + (t.toSpecialLinearGroup : Matrix ι ι F) = t.toMatrix := rfl + +@[simp] +lemma toSpecialLinearGroup_mk {i j : ι} (hij : i ≠ j) (c : F) : + (TransvectionStruct.mk i j hij c).toSpecialLinearGroup = + SpecialLinearGroup.transvection hij c := rfl + +end TransvectionStruct + end transvection section SL2 @@ -545,6 +583,8 @@ variable {F : Type*} [Field F] open MatrixGroups +namespace SpecialLinearGroup + /-- An element in SLₙ(F) induced by a diagonal matrix `1` on any other entries and `a`, `a⁻¹` on positition `i` and `j` respectively where `i ≠ j`. -/ noncomputable def diag2n {ι : Type*} [Fintype ι] [DecidableEq ι] {i j : ι} (hij : i ≠ j) (a : F) @@ -562,18 +602,23 @@ lemma diag2n_coe {ι : Type*} [Fintype ι] [DecidableEq ι] {i j : ι} (hij : i noncomputable abbrev diag2 (a : F) (ha : a ≠ 0) : SL(2, F) := diag2n zero_ne_one a ha +lemma diag2_def {a : F} (ha : a ≠ 0) : diag2 a ha = diag2n zero_ne_one a ha := rfl + lemma diag2_coe (a : F) (ha : a ≠ 0) : (diag2 a ha).1 = diagonal (fun i ↦ match i with | 0 => a|1 => a⁻¹) := by simp [diag2n_coe] -lemma diag2_mulVec_single_i₁ (a : F) (ha : a ≠ 0) : - (diag2 a ha).1 *ᵥ (Pi.single 0 (1 : F)) = a • Pi.single 0 (1 : F) := by - ext k; fin_cases k <;> simp [diag2_coe] +lemma diag2_coe' {a : F} (ha : a ≠ 0) : + (diag2 a ha).1 = !![a, 0; 0, a⁻¹] := by + ext i j + fin_cases i <;> fin_cases j <;> simp [diag2n_coe] + +lemma diag2_smul_single_i₁ {a : F} (ha : a ≠ 0) : + diag2 a ha • (Pi.single 0 1 : Fin 2 → F) = a • Pi.single 0 (1 : F) := by + ext k; fin_cases k <;> simp [Matrix.SpecialLinearGroup.smul_def, diag2_coe] -lemma diag2_mulVec_single_i₂ - (a : F) (ha : a ≠ 0) : - (diag2 a ha).1 *ᵥ (Pi.single 1 (1 : F)) = - a⁻¹ • Pi.single 1 (1 : F) := by - ext k; fin_cases k <;> simp [diag2_coe] +lemma diag2_smul_single_i₂ {a : F} (ha : a ≠ 0) : + diag2 a ha • (Pi.single 1 1 : Fin 2 → F) = a⁻¹ • Pi.single 1 (1 : F) := by + ext k; fin_cases k <;> simp [Matrix.SpecialLinearGroup.smul_def, diag2_coe] lemma diag2_mul_inv (a : F) (ha : a ≠ 0) : diag2 a ha * diag2 a⁻¹ (inv_ne_zero ha) = 1 := Subtype.ext <| by @@ -584,6 +629,102 @@ lemma diag2_inv (a : F) (ha : a ≠ 0) : apply inv_eq_of_mul_eq_one_right exact diag2_mul_inv a ha +section induction + +variable {ι R : Type*} [Fintype ι] [DecidableEq ι] [CommRing R] + +/-- the coercion to `Matrix ι ι R` as a monoid homomorphism -/ +def coeMonoidHom : SpecialLinearGroup ι R →* Matrix ι ι R where + toFun := Subtype.val + map_one' := rfl + map_mul' _ _ := rfl + +@[simp] +lemma coeMonoidHom_apply (g : SpecialLinearGroup ι R) : coeMonoidHom g = (g : Matrix ι ι R) := rfl + +lemma coeMonoidHom_injective : Function.Injective (coeMonoidHom : SpecialLinearGroup ι R → _) := + Subtype.val_injective + +private lemma diag_decompose (i₀ : ι) (D : ι → F) (hD : det (diagonal D) = 1) : + Finset.prod {i | i ≠ i₀} (fun i k ↦ if k = i then D i else + if k = i₀ then (D i)⁻¹ else 1 : ι → ι → F) = D := by + rw [det_diagonal, show Finset.univ = insert i₀ ({i | i ≠ i₀} : Finset ι) by grind, + Finset.prod_insert (by grind), mul_eq_one_iff_eq_inv₀ (by grind), + ← Finset.prod_inv_distrib] at hD + ext x + by_cases hx : x = i₀ + · simpa [hx, hD, -Finset.prod_inv_distrib] using Finset.prod_congr rfl (by grind) + · simp [hx] + +lemma diagonal_neZero (D : ι → F) (hD : det (diagonal D) = 1) (j : ι) : + D j ≠ 0 := fun h ↦ by + rw [det_diagonal, show Finset.univ = insert j ({i | i ≠ j} : Finset ι) by grind, + Finset.prod_insert (by grind), h, zero_mul] at hD + exact zero_ne_one hD + +lemma diag_commute (i₀ : ι) (D : ι → F) (hD : det (diagonal D) = 1) : + (({i | i ≠ i₀} : Finset ι) : Set ι).Pairwise (Function.onFun Commute fun i ↦ + if hi : i ≠ i₀ then diag2n hi (D i) (diagonal_neZero D hD i) else 1) := by + intro i1 hi1 i2 hi2 hi12 + ext i j + simp [apply_dite, diag2n_coe] + split_ifs <;> simp [diagonal_apply]; grind + +lemma diag_eq_diag2n_prod (i₀ : ι) (D : ι → F) (hD : det (diagonal D) = 1) : + (⟨diagonal D, hD⟩ : SpecialLinearGroup ι F) = + Finset.noncommProd {i : ι | i ≠ i₀} (fun i ↦ if hi : i ≠ i₀ then + diag2n hi (D i) (diagonal_neZero D hD i) else 1) (diag_commute i₀ D hD) := by + classical + set g : ι → ι → F := fun i k ↦ if k = i then D i else if k = i₀ then (D i)⁻¹ else 1 with hg_def + apply coeMonoidHom_injective + rw [Finset.map_noncommProd] + simp_rw [coeMonoidHom_apply, apply_dite, coe_one] + rw [Finset.noncommProd_congr (s₂ := {i | i ≠ i₀}) rfl (fun i hi ↦ + (dif_pos (Finset.mem_filter.1 hi).2 : _ = (diag2n (Finset.mem_filter.1 hi).2 _ _).1))] + convert_to! _ = Finset.noncommProd {i | i ≠ i₀} (fun x ↦ diagonal (g x)) _ + simp_rw [← diagonalRingHom_apply] + rw [← Finset.map_noncommProd _ _ (fun _ _ _ _ _ ↦ Commute.all _ _), Finset.noncommProd_eq_prod] + rw [diag_decompose i₀ D hD] + +/-- The `SpecialLinearGroup` analogue of + `Matrix.Pivot.exists_list_transvec_mul_diagonal_mul_list_transvec`: + every element of `SL(ι, F)` is a product of transvections, + a diagonal matrix of determinant `1`, and transvections. -/ +theorem exists_list_transvec_mul_diagonal_mul_list_transvec (M : SpecialLinearGroup ι F) : + ∃ (L L' : List (TransvectionStruct ι F)) (D : ι → F) (hD : det (diagonal D) = 1), + M = (L.map TransvectionStruct.toSpecialLinearGroup).prod * ⟨diagonal D, hD⟩ * + (L'.map TransvectionStruct.toSpecialLinearGroup).prod := by + obtain ⟨L, L', D, hM⟩ := Pivot.exists_list_transvec_mul_diagonal_mul_list_transvec M.1 + refine ⟨L, L', D, by simpa [hM] using M.2, Subtype.ext <| ?_⟩ + simp_rw [coe_mul, ← coeMonoidHom_apply, map_list_prod, List.map_map, Function.comp_def, + coeMonoidHom_apply, TransvectionStruct.toSpecialLinearGroup_coe, hM] + +theorem diagonal_transvection_induction' [Nontrivial ι] (P : SpecialLinearGroup ι F → Prop) + (M : SpecialLinearGroup ι F) + (hdiag : ∀ (i j : ι) (hij : i ≠ j) {c : F} (hc : c ≠ 0), P (diag2n hij c hc)) + (htransvec : ∀ (i j : ι) (hij : i ≠ j) (a : F), P (transvection hij a)) + (hmul : ∀ A B, P A → P B → P (A * B)) : P M := by + obtain ⟨i₀, j₀, hij₀⟩ := exists_pair_ne ι + have hP1 : P 1 := transvection_coeff_zero (F := F) hij₀ ▸ htransvec i₀ j₀ hij₀ 0 + have hdiagonal (D : ι → F) (hD : det (diagonal D) = 1) : P ⟨diagonal D, hD⟩ := by + rw [diag_eq_diag2n_prod i₀ D hD] + refine Finset.noncommProd_induction _ _ _ P hmul hP1 fun i hi => ?_ + simp [(Finset.mem_filter.1 hi).2, hdiag] + have hlist (L : List (TransvectionStruct ι F)) : + P (L.map TransvectionStruct.toSpecialLinearGroup).prod := by + induction L with + | nil => simpa using hP1 + | cons t L ih => + rw [List.map_cons, List.prod_cons, t.toSpecialLinearGroup_def] + exact hmul _ _ (htransvec t.i t.j t.hij t.c) ih + obtain ⟨L, L', D, hD, hM⟩ := exists_list_transvec_mul_diagonal_mul_list_transvec M + exact hM ▸ hmul _ _ (hmul _ _ (hlist L) (hdiagonal D hD)) (hlist L') + +end induction + +end SpecialLinearGroup + +open Matrix.SpecialLinearGroup open scoped commutatorElement lemma commutator_diag2_transvection (a : F) (ha : a ≠ 0) (b c : F) @@ -598,12 +739,67 @@ lemma commutator_diag2_transvection (a : F) (ha : a ≠ 0) (b c : F) /-- For any `c : F`, given `a ≠ 0` and `a² ≠ 1`, the transvection `transvection i₁ i₂ hij c` is a commutator in `SL ι F`, hence lies in `commutator (SL ι F)`. -/ -lemma transvection_mem_commutator (a : F) (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) (c : F) : +lemma transvection_mem_commutator₀ {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) (c : F) : SpecialLinearGroup.transvection zero_ne_one c ∈ commutator SL(2, F) := by rw [← commutator_diag2_transvection a ha (c / (a ^ 2 - 1)) c (div_mul_cancel₀ c (sub_ne_zero_of_ne hasq)).symm] exact Subgroup.commutator_mem_commutator (Subgroup.mem_top _) (Subgroup.mem_top _) +lemma transvection_mem_commutator₁ {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) (c : F) : + SpecialLinearGroup.transvection one_ne_zero c ∈ commutator SL(2, F) := by + have (b c' : F) (hc : c' = b * (a ^ 2 - 1)) : + ⁅diag2 a⁻¹ (inv_ne_zero ha), SpecialLinearGroup.transvection one_ne_zero b⁆ = + (SpecialLinearGroup.transvection one_ne_zero c' : SL(2, F)) := by + rw [commutatorElement_def, diag2_inv a⁻¹ (inv_ne_zero ha), + SpecialLinearGroup.transvection_inv one_ne_zero b] + refine Subtype.ext <| Matrix.ext fun i j ↦ ?_ + fin_cases i <;> fin_cases j <;> + simp [hc, SpecialLinearGroup.transvection_coe, diag2_coe, inv_inv, mul_add, add_mul, + mul_inv_cancel₀ ha, inv_mul_cancel₀ ha, mul_comm a b, mul_assoc b a a, ← pow_two, + mul_sub_one, ← sub_eq_add_neg] + rw [← this (c / (a ^ 2 - 1)) c (div_mul_cancel₀ c (sub_ne_zero_of_ne hasq)).symm] + exact Subgroup.commutator_mem_commutator (Subgroup.mem_top _) (Subgroup.mem_top _) + +lemma transvection_mem_commutator {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) {i j : Fin 2} (h : i ≠ j) + (c : F) : SpecialLinearGroup.transvection h c ∈ commutator SL(2, F) := by + fin_cases i + · obtain rfl : j = 1 := by fin_cases j <;> tauto + exact transvection_mem_commutator₀ ha hasq c + · obtain rfl : j = 0 := by fin_cases j <;> tauto + exact transvection_mem_commutator₁ ha hasq c + +lemma diag2_decompose (a : F) (ha : a ≠ 0) : + diag2 a ha = SpecialLinearGroup.transvection zero_ne_one a * + SpecialLinearGroup.transvection one_ne_zero (- a⁻¹) * + SpecialLinearGroup.transvection zero_ne_one a * + SpecialLinearGroup.transvection zero_ne_one (-1) * + SpecialLinearGroup.transvection one_ne_zero 1 * + SpecialLinearGroup.transvection zero_ne_one (-1) := by + ext i j + fin_cases i <;> fin_cases j <;> + simp [diag2_coe', transvection_coe, mul_add, add_mul, mul_inv_cancel₀ ha, inv_mul_cancel₀ ha] + +theorem SL2.transvection_induction (P : SL(2, F) → Prop) + (htransvec : ∀ (i j : Fin 2) (h : i ≠ j) c, P (SpecialLinearGroup.transvection h c)) + (hmul : ∀ A B, P A → P B → P (A * B)) (A : SL(2, F)) : P A := by + refine diagonal_transvection_induction' P _ (fun i j hij c hc ↦ ?_) htransvec hmul + fin_cases i + · obtain rfl : j = 1 := by fin_cases j <;> tauto + change P (diag2 c hc) + rw [diag2_decompose c hc] + refine hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ ?_ ?_) ?_) ?_) ?_) ?_ + all_goals exact htransvec _ _ _ _ + · obtain rfl : j = 0 := by fin_cases j <;> tauto + rw [show diag2n hij c hc = diag2 c⁻¹ (inv_ne_zero hc) by + ext; simp [diag2n_coe, diagonal_apply]; grind, diag2_decompose c⁻¹ (inv_ne_zero hc)] + refine hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ (hmul _ _ ?_ ?_) ?_) ?_) ?_) ?_ + all_goals exact htransvec _ _ _ _ + +lemma SL2.commutator_eq_top {a : F} (ha : a ≠ 0) (hasq : a ^ 2 ≠ 1) : + commutator SL(2, F) = ⊤ := + le_antisymm le_top (fun A _ ↦ SL2.transvection_induction _ + (fun _ _ ↦ transvection_mem_commutator ha hasq) (fun _ _ ↦ mul_mem) A) + end SL2 end Matrix diff --git a/Mathlib/LinearAlgebra/Matrix/ToLinearEquiv.lean b/Mathlib/LinearAlgebra/Matrix/ToLinearEquiv.lean index fd43268977d2e8..0fff49a15762ba 100644 --- a/Mathlib/LinearAlgebra/Matrix/ToLinearEquiv.lean +++ b/Mathlib/LinearAlgebra/Matrix/ToLinearEquiv.lean @@ -158,54 +158,61 @@ private theorem exists_mulVec_eq_zero_iff' {A : Type*} (K : Type*) [DecidableEq RingHom.mapMatrix_apply, Pi.smul_apply, smul_eq_mul, Algebra.smul_def] · rw [mulVec_smul, mul_eq, Pi.smul_apply, Pi.zero_apply, smul_zero] -theorem exists_mulVec_eq_zero_iff {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A] - {M : Matrix n n A} : (∃ v ≠ 0, M *ᵥ v = 0) ↔ M.det = 0 := +variable {A : Type*} [CommRing A] [IsDomain A] {M N : Matrix n n A} + +theorem exists_mulVec_eq_zero_iff [DecidableEq n] : (∃ v ≠ 0, M *ᵥ v = 0) ↔ M.det = 0 := exists_mulVec_eq_zero_iff' (FractionRing A) -theorem exists_vecMul_eq_zero_iff {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A] - {M : Matrix n n A} : (∃ v ≠ 0, v ᵥ* M = 0) ↔ M.det = 0 := by +theorem exists_vecMul_eq_zero_iff [DecidableEq n] : (∃ v ≠ 0, v ᵥ* M = 0) ↔ M.det = 0 := by simpa only [← M.det_transpose, ← mulVec_transpose] using exists_mulVec_eq_zero_iff -theorem nondegenerate_iff_det_ne_zero {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A] - {M : Matrix n n A} : Nondegenerate M ↔ M.det ≠ 0 := by - refine ⟨?_, nondegenerate_of_det_ne_zero⟩ - rw [ne_eq, ← exists_vecMul_eq_zero_iff] - push Not - intro hM v hv hMv - obtain ⟨w, hwMv⟩ := hM.exists_not_ortho_of_ne_zero hv - simp [dotProduct_mulVec, hMv, zero_dotProduct, ne_eq] at hwMv - -lemma separatingLeft_iff_det_ne_zero {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A] - {M : Matrix n n A} : SeparatingLeft M ↔ M.det ≠ 0 := by - refine ⟨fun h hc ↦ ?_, fun h ↦ (nondegenerate_of_det_ne_zero h).1⟩ - obtain ⟨v, hvne, hv⟩ := exists_vecMul_eq_zero_iff.mpr hc - refine hvne (separatingLeft_def.mp h v ?_) - simp [dotProduct_mulVec, hv] - -lemma separatingRight_iff_det_ne_zero {A : Type*} [DecidableEq n] [CommRing A] [IsDomain A] - {M : Matrix n n A} : SeparatingRight M ↔ M.det ≠ 0 := by - refine ⟨fun h hc ↦ ?_, fun h ↦ (nondegenerate_of_det_ne_zero h).2⟩ - obtain ⟨v, hvne, hv⟩ := exists_mulVec_eq_zero_iff.mpr hc - refine hvne (separatingRight_def.mp h v ?_) - simp [hv] - -theorem Nondegenerate.mul_iff_right {A : Type*} [CommRing A] [IsDomain A] - {M N : Matrix n n A} (h : N.Nondegenerate) : +theorem nondegenerate_iff_det_ne_zero [DecidableEq n] : Nondegenerate M ↔ M.det ≠ 0 := by + grind [nondegenerate_iff_forall_vecMul_and_mulVec_eq_zero, exists_mulVec_eq_zero_iff, + exists_vecMul_eq_zero_iff] + +lemma separatingLeft_iff_det_ne_zero [DecidableEq n] : SeparatingLeft M ↔ M.det ≠ 0 := by + grind [separatingLeft_iff_forall_vecMul_eq_zero, exists_vecMul_eq_zero_iff] + +lemma separatingRight_iff_det_ne_zero [DecidableEq n] : SeparatingRight M ↔ M.det ≠ 0 := by + grind [separatingRight_iff_forall_mulVec_eq_zero, exists_mulVec_eq_zero_iff] + +omit [Fintype n] in +theorem nondegenerate_iff_separatingLeft [Finite n] : M.Nondegenerate ↔ M.SeparatingLeft := by + classical + have := Fintype.ofFinite n + rw [nondegenerate_iff_det_ne_zero, separatingLeft_iff_det_ne_zero] + +alias ⟨_, SeparatingLeft.nondegenerate⟩ := nondegenerate_iff_separatingLeft + +omit [Fintype n] in +theorem nondegenerate_iff_separatingRight [Finite n] : M.Nondegenerate ↔ M.SeparatingRight := by + classical + have := Fintype.ofFinite n + rw [nondegenerate_iff_det_ne_zero, separatingRight_iff_det_ne_zero] + +alias ⟨_, SeparatingRight.nondegenerate⟩ := nondegenerate_iff_separatingRight + +omit [Fintype n] in +theorem separatingLeft_iff_separatingRight [Finite n] : M.SeparatingLeft ↔ M.SeparatingRight := + nondegenerate_iff_separatingLeft.symm.trans nondegenerate_iff_separatingRight + +alias ⟨SeparatingLeft.separatingRight, SeparatingRight.separatingLeft⟩ := + separatingLeft_iff_separatingRight + +theorem Nondegenerate.mul_iff_right (h : N.Nondegenerate) : (M * N).Nondegenerate ↔ M.Nondegenerate := by classical simp only [nondegenerate_iff_det_ne_zero, det_mul] at h ⊢ exact mul_ne_zero_iff_right h -theorem Nondegenerate.mul_iff_left {A : Type*} [CommRing A] [IsDomain A] - {M N : Matrix n n A} (h : M.Nondegenerate) : +theorem Nondegenerate.mul_iff_left (h : M.Nondegenerate) : (M * N).Nondegenerate ↔ N.Nondegenerate := by classical simp only [nondegenerate_iff_det_ne_zero, det_mul] at h ⊢ exact mul_ne_zero_iff_left h omit [Fintype n] in -theorem Nondegenerate.smul_iff [Finite n] {A : Type*} [CommRing A] [IsDomain A] - {M : Matrix n n A} {t : A} (h : t ≠ 0) : +theorem Nondegenerate.smul_iff [Finite n] {t : A} (h : t ≠ 0) : (t • M).Nondegenerate ↔ M.Nondegenerate := by have := Fintype.ofFinite rw [nondegenerate_def, nondegenerate_def] diff --git a/Mathlib/LinearAlgebra/Matrix/ZMatrix.lean b/Mathlib/LinearAlgebra/Matrix/ZMatrix.lean index 6fa0a0046618da..ce22815bdb65b3 100644 --- a/Mathlib/LinearAlgebra/Matrix/ZMatrix.lean +++ b/Mathlib/LinearAlgebra/Matrix/ZMatrix.lean @@ -16,8 +16,8 @@ A matrix whose off-diagonal entries are all non-positive is known as a Z-matrix. are examples of Z-matrices. ## Main results: - * `Matrix.lt_two_mul_of_mul_diagonal_posDef_of_for_le_of_hasEigen`: a spectral bound result for - Z-matrices satisfying a positive-definiteness condition. +* `Matrix.lt_two_mul_of_mul_diagonal_posDef_of_for_le_of_hasEigen`: a spectral bound result for + Z-matrices satisfying a positive-definiteness condition. -/ diff --git a/Mathlib/LinearAlgebra/Matrix/ZPow.lean b/Mathlib/LinearAlgebra/Matrix/ZPow.lean index 14df76dae44d4f..0ba75ca5ab6ff3 100644 --- a/Mathlib/LinearAlgebra/Matrix/ZPow.lean +++ b/Mathlib/LinearAlgebra/Matrix/ZPow.lean @@ -97,8 +97,7 @@ theorem inv_zpow (A : M) : ∀ n : ℤ, A⁻¹ ^ n = (A ^ n)⁻¹ @[simp] theorem zpow_neg_one (A : M) : A ^ (-1 : ℤ) = A⁻¹ := by - convert! DivInvMonoid.zpow_neg' 0 A - simp only [zpow_one, Int.ofNat_zero, Int.natCast_succ, zpow_eq_pow, zero_add] + simpa using DivInvMonoid.zpow_neg' 0 A @[simp] theorem zpow_neg_natCast (A : M) (n : ℕ) : A ^ (-n : ℤ) = (A ^ n)⁻¹ := by @@ -250,7 +249,7 @@ theorem zpow_ne_zero_of_isUnit_det [Nonempty n'] [Nontrivial R] {A : M} (ha : Is (z : ℤ) : A ^ z ≠ 0 := by have := ha.det_zpow z contrapose this - rw [this, det_zero ‹_›] + rw [this, det_zero] exact not_isUnit_zero theorem zpow_sub {A : M} (ha : IsUnit A.det) (z1 z2 : ℤ) : A ^ (z1 - z2) = A ^ z1 / A ^ z2 := by diff --git a/Mathlib/LinearAlgebra/Multilinear/Curry.lean b/Mathlib/LinearAlgebra/Multilinear/Curry.lean index c4c1768e87dc5c..6b7e3551c8a396 100644 --- a/Mathlib/LinearAlgebra/Multilinear/Curry.lean +++ b/Mathlib/LinearAlgebra/Multilinear/Curry.lean @@ -10,6 +10,7 @@ public import Mathlib.LinearAlgebra.Multilinear.Basic /-! # Currying of multilinear maps + We register isomorphisms corresponding to currying or uncurrying variables, transforming a multilinear function `f` on `n+1` variables into a linear function taking values in multilinear functions in `n` variables, and into a multilinear function in `n` variables taking values in linear diff --git a/Mathlib/LinearAlgebra/PiTensorProduct.lean b/Mathlib/LinearAlgebra/PiTensorProduct.lean new file mode 100644 index 00000000000000..68ff7ebd53b1ad --- /dev/null +++ b/Mathlib/LinearAlgebra/PiTensorProduct.lean @@ -0,0 +1,5 @@ +module + +public import Mathlib.LinearAlgebra.PiTensorProduct.Basic + +deprecated_module (since := "2026-06-18") diff --git a/Mathlib/LinearAlgebra/Prod.lean b/Mathlib/LinearAlgebra/Prod.lean index 21f532a1db5fc0..67074813e619c5 100644 --- a/Mathlib/LinearAlgebra/Prod.lean +++ b/Mathlib/LinearAlgebra/Prod.lean @@ -765,6 +765,7 @@ protected def prodCongr : (M × M₃) ≃ₗ[R] M₂ × M₄ := { e₁.toAddEquiv.prodCongr e₂.toAddEquiv with map_smul' := fun c _x => Prod.ext (e₁.map_smulₛₗ c _) (e₂.map_smulₛₗ c _) } +@[simp] theorem prodCongr_symm : (e₁.prodCongr e₂).symm = e₁.symm.prodCongr e₂.symm := rfl diff --git a/Mathlib/LinearAlgebra/Projectivization/PSL/PSL2.lean b/Mathlib/LinearAlgebra/Projectivization/PSL/PSL2.lean new file mode 100644 index 00000000000000..32a6e3158224af --- /dev/null +++ b/Mathlib/LinearAlgebra/Projectivization/PSL/PSL2.lean @@ -0,0 +1,137 @@ +/- +Copyright (c) 2026 Yunzhou Xie. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Edison Xie +-/ +module + +public import Mathlib.GroupTheory.GroupAction.Iwasawa +public import Mathlib.GroupTheory.IsPerfect +public import Mathlib.LinearAlgebra.Projectivization.PSL.Stabilizer + +/-! +-/ + +@[expose] public section + +variable {ι F : Type*} [Field F] [DecidableEq ι] [Fintype ι] + +open Matrix Matrix.SpecialLinearGroup + +open scoped MatrixGroups + +namespace SL2Gen + +/-- A transvection `transvection i j hij b` lies in `lineStab (span F {Pi.single i 1})`. -/ +lemma transvection_mem_lineStab {i j : ι} (hij : i ≠ j) (b : F) : + transvection hij b ∈ lineStab (Submodule.span F {(Pi.single i (1 : F) : ι → F)}) := + fun w ↦ Submodule.mem_span_singleton.2 ⟨b * w j, by simp [mul_smul, + Matrix.SpecialLinearGroup.smul_def, transvection_coe, add_smul, Matrix.single_mulVec_eq]⟩ + +/-- Every transvection in `SL ι F` whose indices are `(i₁, i₂)` or `(i₂, i₁)` is in the join +of `lineStab(span F {e_{i₁}})` and `lineStab(span F {e_{i₂}})`. -/ +lemma transvection_mem_lineStab_sup (t : TransvectionStruct (Fin 2) F) : + t.toSpecialLinearGroup ∈ + lineStab (Submodule.span F {(Pi.single 0 1 : Fin 2 → F)}) + ⊔ lineStab (Submodule.span F {(Pi.single 1 1 : Fin 2 → F)}) := by + obtain ⟨i, j, hij, c⟩ := t + simp only [Fin.isValue, TransvectionStruct.toSpecialLinearGroup_mk] + fin_cases i <;> fin_cases j <;> try tauto + · exact Subgroup.mem_sup_left <| transvection_mem_lineStab zero_ne_one c + · exact Subgroup.mem_sup_right <| transvection_mem_lineStab one_ne_zero c + +/-- SL-level generation: in the 2-element-index case, the join of the two `lineStab` subgroups +attached to the two coordinate axes is all of `SL ι F`. -/ +lemma SL_card_two_lineStab_sup_eq_top : + lineStab (Submodule.span F {(Pi.single 0 1: Fin 2 → F)}) ⊔ + lineStab (Submodule.span F {(Pi.single 1 1: Fin 2 → F)}) = + (⊤ : Subgroup SL(2, F)) := + le_antisymm le_top fun M _ ↦ SL2.transvection_induction _ + (fun i j hij a ↦ by simpa using transvection_mem_lineStab_sup ⟨i, j, hij, a⟩) + (fun _ _ ↦ mul_mem) M + +end SL2Gen + +open scoped LinearAlgebra.Projectivization + +/-- At the SL level: when `Fintype.card ι = 2`, the supremum over all projective points of +the `lineStab` subgroups equals `⊤` in `SL ι F`. -/ +lemma PSL.iSup_lineStab_eq_top : + (⨆ p : ℙ F (Fin 2 → F), lineStab p.submodule) = (⊤ : Subgroup SL(2, F)) := by + refine le_antisymm le_top (SL2Gen.SL_card_two_lineStab_sup_eq_top (F := F) ▸ + sup_le ?_ ?_) + <;> rw [← Projectivization.submodule_mk (K := F) _ (Pi.single_ne_zero_iff.2 one_ne_zero)] + <;> exact le_iSup_iff.2 fun b a ↦ a _ + +/-- The Iwasawa generator property: when `Fintype.card ι = 2`, the supremum of the +`iwasawaT` subgroups equals all of `PSL`. -/ +lemma PSL.iSup_iwasawaT_eq_top : + iSup (PSL.iwasawaT (F := F) (ι := Fin 2)) = ⊤ := by + have step1 : iSup (PSL.iwasawaT (F := F) (ι := Fin 2)) = + Subgroup.map (QuotientGroup.mk' (Subgroup.center (Matrix.SpecialLinearGroup (Fin 2) F))) + (⨆ p : ℙ F (Fin 2 → F), + Matrix.SpecialLinearGroup.lineStab (F := F) (ι := Fin 2) p.submodule) := by + rw [Subgroup.map_iSup] + rw [step1, PSL.iSup_lineStab_eq_top] + exact Subgroup.map_top_of_surjective _ (QuotientGroup.mk'_surjective _) + +open MulAction + +/-- The Iwasawa structure on PSL(2, F). -/ +noncomputable abbrev PSL2.Iwasawa : IwasawaStructure PSL(2, F) (ℙ F (Fin 2 → F)) where + T := PSL.iwasawaT + is_comm p := by + have hSL : IsMulCommutative (lineStab (F := F) (ι := Fin 2) p.submodule) := by + rw [← Projectivization.mk_rep p, Projectivization.submodule_mk] + exact lineStab_isMulCommutative_of_span p.rep p.rep_nonzero + exact Subgroup.map_isMulCommutative _ _ + is_conj g p := by + obtain ⟨g_SL, rfl⟩ := QuotientGroup.mk_surjective g + rw [Matrix.ProjectiveSpecialLinearGroup.smul_proj_mk] + change Subgroup.map _ _ = _ + rw [PSL.smul_submodule, Matrix.SpecialLinearGroup.lineStab_smul, + PSL.iwasawaT_map_conj] + is_generator := PSL.iSup_iwasawaT_eq_top + +namespace SL2Simple + +open Matrix.SpecialLinearGroup + +/-- `commutator (PSL ι F) = ⊤`. -/ +lemma PSL_commutator_eq_top (hF : ∃ a : F, a ≠ 0 ∧ a ^ 2 ≠ 1) : + commutator PSL(2, F) = ⊤ := by + obtain ⟨a, ha, hasq⟩ := hF + haveI : Group.IsPerfect SL(2, F) := ⟨SL2.commutator_eq_top ha hasq⟩ + have : Group.IsPerfect (Matrix.ProjectiveSpecialLinearGroup (Fin 2) F) := inferInstance + exact this.commutator_eq_top + +/-- `PSL ι F` is nontrivial whenever `ι` has at least two elements (and `F` is a field, +hence in particular nontrivial). -/ +instance PSL_nontrivial [Nontrivial ι] : + Nontrivial (Matrix.ProjectiveSpecialLinearGroup ι F) := by + obtain ⟨i₁, i₂, hij⟩ := exists_pair_ne ι + set g : Matrix.SpecialLinearGroup ι F := transvection hij 1 + refine ⟨⟨(QuotientGroup.mk g : Matrix.ProjectiveSpecialLinearGroup ι F), + 1, fun h ↦ one_ne_zero (α := F) ?_⟩⟩ + rwa [QuotientGroup.eq_one_iff, transvection_mem_center_iff] at h + +end SL2Simple + +theorem Matrix.ProjectiveSpecialLinearGroup.rank_two_simple' + (hF : ∃ a : F, a ≠ 0 ∧ a ^ 2 ≠ 1) : + IsSimpleGroup PSL(2, F) := + MulAction.IwasawaStructure.isSimpleGroup + (SL2Simple.PSL_commutator_eq_top hF) PSL2.Iwasawa inferInstance + +private lemma field_cond_of_four_le_card (hF : 4 ≤ Nat.card F) : + ∃ a : F, a ≠ 0 ∧ a ^ 2 ≠ 1 := by + have : Finite F := (Nat.card_pos_iff.1 (by omega)).2 + obtain ⟨x, hx⟩ : IsCyclic Fˣ := by infer_instance + refine ⟨x, Units.ne_zero x, fun h ↦ ?_⟩ + grw [Nat.card_eq_card_units_add_one F, ← orderOf_eq_card_of_forall_mem_zpowers hx, + orderOf_le_of_pow_eq_one zero_lt_two (Units.ext <| by simpa using h)] at hF + omega + +theorem Matrix.ProjectiveSpecialLinearGroup.rank_two_simple (hF : 4 ≤ Nat.card F) : + IsSimpleGroup PSL(2, F) := + Matrix.ProjectiveSpecialLinearGroup.rank_two_simple' (field_cond_of_four_le_card hF) diff --git a/Mathlib/LinearAlgebra/Projectivization/PSL/Stabilizer.lean b/Mathlib/LinearAlgebra/Projectivization/PSL/Stabilizer.lean new file mode 100644 index 00000000000000..2201a9a390797c --- /dev/null +++ b/Mathlib/LinearAlgebra/Projectivization/PSL/Stabilizer.lean @@ -0,0 +1,142 @@ +/- +Copyright (c) 2026 Yunzhou Xie. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Edison Xie +-/ + +module + +public import Mathlib.LinearAlgebra.Projectivization.Action + +/-! +# Stabilizer of a line in PSL(n, F) +This file contains key constructions to prove that `PSL(n, F)` is simple via +showing it has an Iwasawa structure. + +## Main definitions + +* `Matrix.SpecialLinearGroup.lineStab` : the unipotent radical attached to a subspace `L ⊆ ι → F` + defined as the subgroup of `SL ι F` consisting of matrices `A` such that `A - 1` + sends every vector into `L`. + +* `PSL.iwasawaT` : the candidate family of subgroups for the Iwasawa structure on + `PSL ι F` acting on the projective space `ℙ F (ι → F)` from `Matrix.SpecialLinearGroup.lineStab`. + +-/ + +@[expose] public section + +variable {F : Type*} [Field F] {ι : Type*} [DecidableEq ι] [Fintype ι] + +/-- The "unipotent radical" attached to a subspace `L ⊆ ι → F`: the subgroup of +`SL ι F` consisting of matrices `A` such that `A - 1` sends every vector into `L`. +When `L` is one-dimensional this is an abelian subgroup of the stabilizer of `L` in `SL`. -/ +def Matrix.SpecialLinearGroup.lineStab (L : Submodule F (ι → F)) : + Subgroup (SpecialLinearGroup ι F) where + carrier := {A | ∀ w : ι → F, A • w - w ∈ L} + one_mem' := by simp + mul_mem' {A B} hA hB := fun w ↦ by + simp only [Set.mem_setOf_eq, mul_smul] at hA hB ⊢ + rw [show A • B • w - w = ((A • (B • w) - A • w) - (B • w - w)) + + (B • w - w) + (A • w - w) by abel, ← smul_sub] + exact add_mem (add_mem (hA _) (hB w)) (hA w) + inv_mem' {A} hA := fun w ↦ by + convert neg_mem (hA (A⁻¹ • w)) using 1 + rw [← mul_smul, mul_inv_cancel, one_smul, neg_sub] + +@[simp] +lemma Matrix.SpecialLinearGroup.mem_lineStab_iff (A : SpecialLinearGroup ι F) + (L : Submodule F (ι → F)) : A ∈ lineStab L ↔ ∀ w : ι → F, A • w - w ∈ L := + Iff.rfl + +open scoped LinearAlgebra.Projectivization + +/-- The candidate family of subgroups for the Iwasawa structure on +`PSL ι F` acting on the projective space `ℙ F (ι → F)`: the unipotent radical +attached to the line through `p`. -/ +noncomputable abbrev PSL.iwasawaT (p : ℙ F (ι → F)) : + Subgroup (Matrix.ProjectiveSpecialLinearGroup ι F) := + Subgroup.map (QuotientGroup.mk' _) + (Matrix.SpecialLinearGroup.lineStab p.submodule) + +open scoped Pointwise + +lemma PSL.smul_submodule (g : Matrix.SpecialLinearGroup ι F) (p : ℙ F (ι → F)) : + (g • p).submodule = g • p.submodule:= by + induction p using Projectivization.ind with | _ v hv => ?_ + simp [Submodule.ext_iff, Submodule.pointwise_smul_def, Submodule.mem_span_singleton, smul_comm] + +/-- Equivariance of `lineStab` under conjugation by elements of `SL`. -/ +lemma Matrix.SpecialLinearGroup.lineStab_smul + (g : Matrix.SpecialLinearGroup ι F) (L : Submodule F (ι → F)) : + Matrix.SpecialLinearGroup.lineStab (g • L) = + MulAut.conj g • Matrix.SpecialLinearGroup.lineStab L := by + ext A + rw [Subgroup.mem_pointwise_smul_iff_inv_smul_mem] + simp only [mem_lineStab_iff, Submodule.mem_smul_pointwise_iff_exists, MulAut.smul_def, + MulAut.inv_apply, MulAut.conj_symm_apply] + refine ⟨fun hA w ↦ ?_, fun hA w ↦ ⟨g⁻¹ • (A • w - w), ?_, by simp⟩⟩ + · obtain ⟨v, hv, hvw⟩ := hA (g • w) + simp_all [eq_comm (a := g • v), sub_eq_iff_eq_add, mul_smul] + · simpa [mul_smul, smul_sub] using hA (g⁻¹ • w) + +/-- The SL-level equivariance pushed through the quotient: the image in `PSL` of +the conjugate `MulAut.conj g_SL • H` equals `MulAut.conj (mk g_SL) • (image of H)`. -/ +lemma PSL.iwasawaT_map_conj (g : Matrix.SpecialLinearGroup ι F) + (H : Subgroup (Matrix.SpecialLinearGroup ι F)) : + Subgroup.map (QuotientGroup.mk' (Subgroup.center (Matrix.SpecialLinearGroup ι F))) + (MulAut.conj g • H) = + MulAut.conj (QuotientGroup.mk g : Matrix.ProjectiveSpecialLinearGroup ι F) • + Subgroup.map (QuotientGroup.mk' (Subgroup.center (Matrix.SpecialLinearGroup ι F))) H := by + ext x + simp only [Subgroup.mem_map, Subgroup.mem_pointwise_smul_iff_inv_smul_mem, + MulAut.smul_def, MulAut.inv_apply, MulAut.conj_symm_apply, QuotientGroup.mk'_apply] + exact ⟨fun ⟨a, ha, ha'⟩ ↦ ⟨g⁻¹ * a * g, ha, by simp [ha']⟩, + fun ⟨a, ha, hx⟩ ↦ ⟨g * a * g⁻¹, by simp [mul_assoc, ha], by simp [hx, mul_assoc]⟩⟩ + +private lemma LinearMap.exists_restrict_span_singleton_eq_smul_id + {R V : Type*} [CommSemiring R] [AddCommMonoid V] [Module R V] + {v : V} {A : V →ₗ[R] V} (hAv : A v ∈ Submodule.span R {v}) : + ∃ c : R, A v = c • v ∧ ∃ hcomap : Submodule.span R {v} ≤ (Submodule.span R {v}).comap A, + A.restrict hcomap = (c • LinearMap.id : Submodule.span R {v} →ₗ[R] _) := by + obtain ⟨c, hc⟩ := Submodule.mem_span_singleton.1 hAv + refine ⟨c, hc.symm, fun w hw ↦ ?_, LinearMap.ext fun ⟨w, hw⟩ ↦ ?_⟩ + <;> obtain ⟨a, rfl⟩ := Submodule.mem_span_singleton.1 hw + · simpa [Submodule.mem_comap, map_smul] using Submodule.smul_mem _ _ hAv + · simp [Subtype.ext_iff, ← hc, smul_comm a c v] + +lemma Matrix.SpecialLinearGroup.lineStab_fix_of_span + (v : ι → F) (hv : v ≠ 0) + (A : Matrix.SpecialLinearGroup ι F) + (hA : A ∈ lineStab (Submodule.span F {v})) : + A • v = v := by + set L : Submodule F (ι → F) := Submodule.span F {v} + obtain ⟨c, hcv, hcomap, hres⟩ := + LinearMap.exists_restrict_span_singleton_eq_smul_id (A := A.toLin'.toLinearMap) + (by simpa using! add_mem (hA v) (Submodule.mem_span_singleton_self v)) + have hQ : L.mapQ L A.toLin'.toLinearMap hcomap = LinearMap.id := LinearMap.ext fun x ↦ by + induction x using Submodule.Quotient.induction_on with + | _ w => simpa [Submodule.Quotient.eq] using! hA w + have hdet := A.toLin'.toLinearMap.det_eq_det_mul_det L hcomap + rw [show LinearMap.det A.toLin'.toLinearMap = 1 by simp [toLin'_to_linearMap], + hres, hQ, LinearMap.det_smul, finrank_span_singleton hv, pow_one, + LinearMap.det_id, LinearMap.det_id, mul_one, mul_one] at hdet + exact hcv.trans (hdet ▸ one_smul F v) + +/-- The subgroup `lineStab (span F {v})` is commutative when `v ≠ 0`. -/ +lemma Matrix.SpecialLinearGroup.lineStab_isMulCommutative_of_span' + (v : ι → F) (hv : v ≠ 0) (A B : SpecialLinearGroup ι F) + (hA : A ∈ SpecialLinearGroup.lineStab (Submodule.span F {v})) + (hB : B ∈ SpecialLinearGroup.lineStab (Submodule.span F {v})) : + A * B = B * A := by + refine Subtype.ext <| ext_iff_smul.2 fun w ↦ ?_ + obtain ⟨α, hα⟩ := Submodule.mem_span_singleton.mp (hA w) + obtain ⟨β, hβ⟩ := Submodule.mem_span_singleton.mp (hB w) + simp only [coe_mul, mul_smul, ← Matrix.SpecialLinearGroup.smul_def] + rw [← sub_add_cancel (A • w) w, ← hα, ← sub_add_cancel (B • w) w, + ← hβ, smul_add, smul_add, ← sub_left_inj (a := w), ← add_sub, ← hα, ← add_sub, ← hβ, + smul_comm, lineStab_fix_of_span v hv A hA, smul_comm, lineStab_fix_of_span v hv B hB, add_comm] + +lemma Matrix.SpecialLinearGroup.lineStab_isMulCommutative_of_span + (v : ι → F) (hv : v ≠ 0) : IsMulCommutative (lineStab (Submodule.span F {v})) := + ⟨⟨fun ⟨A, hA⟩ ⟨B, hB⟩ ↦ by simpa using lineStab_isMulCommutative_of_span' v hv A B hA hB⟩⟩ diff --git a/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean b/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean index 7a28de8535feb4..1bd2bf12f0d792 100644 --- a/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean +++ b/Mathlib/LinearAlgebra/QuadraticForm/Basic.lean @@ -6,6 +6,7 @@ Authors: Anne Baanen, Kexing Ying, Eric Wieser module public import Mathlib.Data.Finset.Sym +public import Mathlib.LinearAlgebra.SesquilinearForm.Orthogonal public import Mathlib.LinearAlgebra.BilinearMap public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas public import Mathlib.LinearAlgebra.Matrix.Determinant.Basic diff --git a/Mathlib/LinearAlgebra/Ray.lean b/Mathlib/LinearAlgebra/Ray.lean index e857c07e686827..9e85bdedf8806f 100644 --- a/Mathlib/LinearAlgebra/Ray.lean +++ b/Mathlib/LinearAlgebra/Ray.lean @@ -464,6 +464,19 @@ theorem units_inv_smul (u : Rˣ) (v : Module.Ray R M) : u⁻¹ • v = u • v : u⁻¹ • v = (u * u) • u⁻¹ • v := Eq.symm <| (u⁻¹ • v).units_smul_of_pos _ (by exact this) _ = u • v := by rw [mul_smul, smul_inv_smul] +/-- Two scalar multiples of a common vector whose coefficients have nonnegative product +lie on a common ray. -/ +theorem sameRay_smul_smul_of_mul_nonneg {v : M} {c₁ c₂ : R} (h : 0 ≤ c₁ * c₂) : + SameRay R (c₁ • v) (c₂ • v) := by + rcases eq_or_ne c₁ 0 with hc₁ | hc₁ + · rw [hc₁, zero_smul]; exact SameRay.zero_left _ + rcases eq_or_ne c₂ 0 with hc₂ | hc₂ + · rw [hc₂, zero_smul]; exact SameRay.zero_right _ + have hpos : 0 < c₁ * c₂ := h.lt_of_ne (mul_ne_zero hc₁ hc₂).symm + rcases mul_pos_iff.mp hpos with ⟨h₁, h₂⟩ | ⟨h₁, h₂⟩ + · exact Or.inr (Or.inr ⟨c₂, c₁, h₂, h₁, by module⟩) + · exact Or.inr (Or.inr ⟨-c₂, -c₁, neg_pos.2 h₂, neg_pos.2 h₁, by module⟩) + section variable [IsTorsionFree R M] diff --git a/Mathlib/LinearAlgebra/Reflection.lean b/Mathlib/LinearAlgebra/Reflection.lean index f79b24d5238189..e466bd0d0c813c 100644 --- a/Mathlib/LinearAlgebra/Reflection.lean +++ b/Mathlib/LinearAlgebra/Reflection.lean @@ -21,8 +21,8 @@ public import Mathlib.Tactic.Module Given an element `x` in a module `M` together with a linear form `f` on `M` such that `f x = 2`, the map `y ↦ y - (f y) • x` is an involutive endomorphism of `M`, such that: - 1. the kernel of `f` is fixed, - 2. the point `x` maps to `-x`. +1. the kernel of `f` is fixed, +2. the point `x` maps to `-x`. Such endomorphisms are often called reflections of the module `M`. When `M` carries an inner product for which `x` is perpendicular to the kernel of `f`, then (with mild assumptions) the endomorphism diff --git a/Mathlib/LinearAlgebra/RootSystem/BaseExists.lean b/Mathlib/LinearAlgebra/RootSystem/BaseExists.lean index d367432da18e79..45bc346053560a 100644 --- a/Mathlib/LinearAlgebra/RootSystem/BaseExists.lean +++ b/Mathlib/LinearAlgebra/RootSystem/BaseExists.lean @@ -17,19 +17,19 @@ public import Mathlib.LinearAlgebra.RootSystem.Finite.Lemmas # Existence of bases for crystallographic root systems ## Main results: - * `RootPairing.Base.mk'`: an alternate constructor for `RootPairing.Base` which demands the axioms - for roots but not for coroots. - * `RootPairing.nonempty_base`: base existence proof for reduced crystallographic root systems. +* `RootPairing.Base.mk'`: an alternate constructor for `RootPairing.Base` which demands the axioms + for roots but not for coroots. +* `RootPairing.nonempty_base`: base existence proof for reduced crystallographic root systems. ## Implementation details The proof needs a set of ordered coefficients, even though the ultimate existence statement does not. There are at least two ways to deal with this: - (a) Using the fact that a crystallographic root system induces a `ℚ`-structure, pass to the root - system over `ℚ` defined by `RootPairing.restrictScalarsRat`, and develop a theory of base - change for root system bases. - (b) Introduce a second set of ordered coefficients (ultimately taken to be `ℚ`) and develop a - theory with two sets of coefficients simultaneously in play. +(a) Using the fact that a crystallographic root system induces a `ℚ`-structure, pass to the root + system over `ℚ` defined by `RootPairing.restrictScalarsRat`, and develop a theory of base + change for root system bases. +(b) Introduce a second set of ordered coefficients (ultimately taken to be `ℚ`) and develop a + theory with two sets of coefficients simultaneously in play. It is not really clear which is the better approach but here we opt for approach (b) as it seems to yield slightly more general results. diff --git a/Mathlib/LinearAlgebra/RootSystem/Defs.lean b/Mathlib/LinearAlgebra/RootSystem/Defs.lean index 1b5b341eac3111..3dd44ce2f78b71 100644 --- a/Mathlib/LinearAlgebra/RootSystem/Defs.lean +++ b/Mathlib/LinearAlgebra/RootSystem/Defs.lean @@ -7,6 +7,7 @@ module public import Mathlib.LinearAlgebra.PerfectPairing.Basic public import Mathlib.LinearAlgebra.Reflection +public import Mathlib.Tactic.CrossRefAttribute /-! # Root data and root systems @@ -113,6 +114,7 @@ variable {ι R M N} variable (P : RootPairing ι R M N) (i j : ι) /-- A root system is a root pairing for which the roots and coroots span their ambient modules. -/ +@[wikidata Q534131] class IsRootSystem : Prop where span_root_eq_top : span R (range P.root) = ⊤ span_coroot_eq_top : span R (range P.coroot) = ⊤ diff --git a/Mathlib/LinearAlgebra/RootSystem/Finite/CanonicalBilinear.lean b/Mathlib/LinearAlgebra/RootSystem/Finite/CanonicalBilinear.lean index 59047ee9a4c205..80834459fb174b 100644 --- a/Mathlib/LinearAlgebra/RootSystem/Finite/CanonicalBilinear.lean +++ b/Mathlib/LinearAlgebra/RootSystem/Finite/CanonicalBilinear.lean @@ -10,6 +10,7 @@ public import Mathlib.LinearAlgebra.RootSystem.RootPositive /-! # The canonical bilinear form on a finite root pairing + Given a finite root pairing, we define a canonical map from weight space to coweight space, and the corresponding bilinear form. This form is symmetric and Weyl-invariant, and if the base ring is linearly ordered, then the form is root-positive, positive-semidefinite on the weight space, and diff --git a/Mathlib/LinearAlgebra/RootSystem/Hom.lean b/Mathlib/LinearAlgebra/RootSystem/Hom.lean index 6f2f01644153eb..8bf34ac72f09d8 100644 --- a/Mathlib/LinearAlgebra/RootSystem/Hom.lean +++ b/Mathlib/LinearAlgebra/RootSystem/Hom.lean @@ -10,6 +10,7 @@ public import Mathlib.LinearAlgebra.RootSystem.Defs /-! # Morphisms of root pairings + This file defines morphisms of root pairings, following the definition of morphisms of root data given in SGA III Exp. 21 Section 6. diff --git a/Mathlib/LinearAlgebra/RootSystem/OfBilinear.lean b/Mathlib/LinearAlgebra/RootSystem/OfBilinear.lean index 6e3c62637c840b..e2474956fc6557 100644 --- a/Mathlib/LinearAlgebra/RootSystem/OfBilinear.lean +++ b/Mathlib/LinearAlgebra/RootSystem/OfBilinear.lean @@ -9,6 +9,7 @@ public import Mathlib.LinearAlgebra.RootSystem.Defs /-! # Root pairings made from bilinear forms + A common construction of root systems is given by taking the set of all vectors in an integral lattice for which reflection yields an automorphism of the lattice. In this file, we generalize this construction, replacing the ring of integers with an arbitrary commutative ring and the @@ -160,8 +161,8 @@ def ofBilinear [IsReflexive R M] (B : M →ₗ[R] M →ₗ[R] R) (hNB : LinearMa right_inv := by intro y simp [involutive_reflection (coroot_apply_self B x.2) y] } - reflectionPerm_root x y := by - simp [Module.reflection_apply] + reflectionPerm_root := by + simp [coe_setOf, Module.reflection_apply] reflectionPerm_coroot x y := by simp only [coe_setOf, mem_setOf_eq, Embedding.coeFn_mk, Embedding.subtype_apply, Dual.eval_apply, Equiv.coe_fn_mk] diff --git a/Mathlib/LinearAlgebra/RootSystem/RootPairingCat.lean b/Mathlib/LinearAlgebra/RootSystem/RootPairingCat.lean index db462a0ecddf90..0360126e568c25 100644 --- a/Mathlib/LinearAlgebra/RootSystem/RootPairingCat.lean +++ b/Mathlib/LinearAlgebra/RootSystem/RootPairingCat.lean @@ -10,6 +10,7 @@ public import Mathlib.CategoryTheory.Category.Basic /-! # The category of root pairings + This file defines the category of root pairings, following the definition of category of root data given in SGA III Exp. 21 Section 6. diff --git a/Mathlib/LinearAlgebra/RootSystem/WeylGroup.lean b/Mathlib/LinearAlgebra/RootSystem/WeylGroup.lean index c794d873bf0dad..62346e7cdc23b3 100644 --- a/Mathlib/LinearAlgebra/RootSystem/WeylGroup.lean +++ b/Mathlib/LinearAlgebra/RootSystem/WeylGroup.lean @@ -10,6 +10,7 @@ public import Mathlib.RepresentationTheory.Basic /-! # The Weyl group of a root pairing + This file defines the Weyl group of a root pairing as the subgroup of automorphisms generated by reflection automorphisms. This deviates from the existing literature, which typically defines the Weyl group as the subgroup of linear transformations of the weight space generated by linear diff --git a/Mathlib/LinearAlgebra/Semisimple.lean b/Mathlib/LinearAlgebra/Semisimple.lean index b1866db02932fd..120bc5cbeda127 100644 --- a/Mathlib/LinearAlgebra/Semisimple.lean +++ b/Mathlib/LinearAlgebra/Semisimple.lean @@ -17,8 +17,8 @@ public import Mathlib.RingTheory.SimpleModule.Basic Given an `R`-module `M` together with an `R`-linear endomorphism `f : M → M`, the following two conditions are equivalent: - 1. Every `f`-invariant submodule of `M` has an `f`-invariant complement. - 2. `M` is a semisimple `R[X]`-module, where the action of the polynomial ring is induced by `f`. +1. Every `f`-invariant submodule of `M` has an `f`-invariant complement. +2. `M` is a semisimple `R[X]`-module, where the action of the polynomial ring is induced by `f`. A linear endomorphism `f` satisfying these equivalent conditions is known as a *semisimple* endomorphism. We provide basic definitions and results about such endomorphisms in this file. @@ -55,7 +55,6 @@ section CommRing variable (f : End R M) -set_option backward.isDefEq.respectTransparency false in /-- A linear endomorphism of an `R`-module `M` is called *semisimple* if the induced `R[X]`-module structure on `M` is semisimple. This is equivalent to saying that every `f`-invariant `R`-submodule of `M` has an `f`-invariant complement: see `Module.End.isSemisimple_iff`. -/ @@ -68,7 +67,6 @@ def IsFinitelySemisimple : Prop := variable {f} -set_option backward.isDefEq.respectTransparency false in /-- A linear endomorphism is semisimple if every invariant submodule has in invariant complement. See also `Module.End.isSemisimple_iff`. -/ @@ -82,7 +80,6 @@ lemma isSemisimple_iff : f.IsSemisimple ↔ ∀ p ∈ invtSubmodule f, ∃ q ∈ invtSubmodule f, IsCompl p q := by simp [isSemisimple_iff'] -set_option backward.isDefEq.respectTransparency false in lemma isSemisimple_restrict_iff (p) (hp : p ∈ invtSubmodule f) : IsSemisimple (LinearMap.restrict f hp) ↔ ∀ q ∈ f.invtSubmodule, q ≤ p → ∃ r ≤ p, r ∈ f.invtSubmodule ∧ Disjoint q r ∧ q ⊔ r = p := by @@ -122,7 +119,6 @@ lemma isSemisimple_id [IsSemisimpleModule R M] : IsSemisimple (LinearMap.id : Mo @[simp] lemma isSemisimple_neg : (-f).IsSemisimple ↔ f.IsSemisimple := by simp [isSemisimple_iff, mem_invtSubmodule] -set_option backward.isDefEq.respectTransparency false in variable (f) in protected lemma _root_.LinearEquiv.isSemisimple_iff {M₂ : Type*} [AddCommGroup M₂] [Module R M₂] (g : End R M₂) (e : M ≃ₗ[R] M₂) (he : e ∘ₗ f = g ∘ₗ e) : @@ -132,7 +128,6 @@ protected lemma _root_.LinearEquiv.isSemisimple_iff {M₂ : Type*} [AddCommGroup simp_rw [IsSemisimple, isSemisimpleModule_iff, (Submodule.orderIsoMapComap e).complementedLattice_iff] -set_option backward.isDefEq.respectTransparency false in lemma eq_zero_of_isNilpotent_isSemisimple (hn : IsNilpotent f) (hs : f.IsSemisimple) : f = 0 := by have ⟨n, h0⟩ := hn rw [← aeval_X (R := R) f]; rw [← aeval_X_pow (R := R) f] at h0 @@ -170,7 +165,6 @@ lemma isSemisimple_sub_algebraMap_iff {μ : R} : refine fun p ↦ ⟨fun h x hx ↦ ?_, fun h x hx ↦ p.sub_mem (h hx) (p.smul_mem μ hx)⟩ simpa using p.add_mem (h hx) (p.smul_mem μ hx) -set_option backward.isDefEq.respectTransparency false in lemma IsSemisimple.restrict {p : Submodule R M} (hp : p ∈ f.invtSubmodule) (hf : f.IsSemisimple) : IsSemisimple (f.restrict hp) := by rw [IsSemisimple] at hf ⊢ @@ -224,7 +218,6 @@ lemma IsSemisimple_smul (t : K) (h : f.IsSemisimple) : wlog ht : t ≠ 0; · simp [not_not.mp ht] rwa [IsSemisimple_smul_iff ht] -set_option backward.isDefEq.respectTransparency false in theorem isSemisimple_of_squarefree_aeval_eq_zero {p : K[X]} (hp : Squarefree p) (hpf : aeval f p = 0) : f.IsSemisimple := by rw [← RingHom.mem_ker, ← AEval.annihilator_eq_ker_aeval (M := M), mem_annihilator, @@ -249,13 +242,11 @@ open Algebra variable (hf : f.IsSemisimple) include hf -set_option backward.isDefEq.respectTransparency false in /-- The minimal polynomial of a semisimple endomorphism is square free -/ theorem IsSemisimple.minpoly_squarefree : Squarefree (minpoly K f) := IsRadical.squarefree (minpoly.ne_zero <| IsIntegral.isIntegral _) <| by rw [isRadical_iff_span_singleton, span_minpoly_eq_annihilator]; exact hf.annihilator_isRadical -set_option backward.isDefEq.respectTransparency false in protected theorem IsSemisimple.aeval (p : K[X]) : (aeval f p).IsSemisimple := let R := K[X] ⧸ Ideal.span {minpoly K f} have : Module.Finite K R := diff --git a/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean b/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean index bd8ccfce7388c1..2bdabb1c709851 100644 --- a/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean +++ b/Mathlib/LinearAlgebra/SesquilinearForm/Basic.lean @@ -26,7 +26,6 @@ basic lemmas about construction and elementary calculations are found there. ## Main declarations * `IsSymm`, `IsAlt`: states that a sesquilinear form is symmetric and alternating, respectively -* `orthogonalBilin` provides the orthogonal complement with respect to a sesquilinear map ## References @@ -358,102 +357,8 @@ end Alternating end LinearMap -namespace Submodule - -/-! ### The orthogonal complement -/ - -variable [CommSemiring R] [CommSemiring R₁] [CommSemiring R₂] -variable [AddCommMonoid M] [Module R M] -variable [AddCommMonoid M₁] [Module R₁ M₁] -variable [AddCommMonoid M₂] [Module R₂ M₂] -variable {N L : Submodule R₁ M₁} - -section - -variable {I₁ : R₁ →+* R} {I₂ : R₂ →+* R} {B : M₁ →ₛₗ[I₁] M₂ →ₛₗ[I₂] M} - -variable (B) in -/-- The orthogonal complement of a submodule `N` with respect to some bilinear map is the set of -elements `x` which are orthogonal to all elements of `N`; i.e., for all `y` in `N`, `B x y = 0`. - -Note that for general (neither symmetric nor antisymmetric) bilinear maps this definition has a -chirality; in addition to this "left" orthogonal complement one could define a "right" orthogonal -complement for which, for all `y` in `N`, `B y x = 0`. This variant definition is not currently -provided in mathlib. -/ -def orthogonalBilin (N : Submodule R₁ M₁) : Submodule R₂ M₂ where - carrier := { m | ∀ n ∈ N, B n m = 0 } - zero_mem' x _ := map_zero _ - add_mem' {u v} hu hv x hx := by simp [hu _ hx, hv _ hx] - smul_mem' c y hy x hx := by simp [hy _ hx] - -@[simp] -theorem mem_orthogonalBilin_iff {m : M₂} : m ∈ N.orthogonalBilin B ↔ ∀ n ∈ N, B n m = 0:= - Iff.rfl - -theorem orthogonalBilin_le (h : N ≤ L) : L.orthogonalBilin B ≤ N.orthogonalBilin B := - fun _ hn l hl ↦ hn l (h hl) - -end - -section - -variable {I₁ : R₁ →+* R} {I₂ : R₁ →+* R} {B : M₁ →ₛₗ[I₁] M₁ →ₛₗ[I₂] M} - -theorem le_orthogonalBilin_orthogonalBilin (b : B.IsRefl) : - N ≤ (N.orthogonalBilin B).orthogonalBilin B := fun n hn _m hm ↦ b _ _ (hm n hn) - -end - -end Submodule - namespace LinearMap -section Orthogonal - -variable [Field K] [AddCommGroup V] [Module K V] [Field K₁] [AddCommGroup V₁] [Module K₁ V₁] - [AddCommGroup V₂] [Module K V₂] {J : K →+* K} {J₁ : K₁ →+* K} {J₁' : K₁ →+* K} - --- ↓ This lemma only applies in fields as we require `a * b = 0 → a = 0 ∨ b = 0` -theorem span_singleton_inf_orthogonal_eq_bot (B : V₁ →ₛₗ[J₁] V₁ →ₛₗ[J₁'] V₂) (x : V₁) - (hx : B x x ≠ 0) : (K₁ ∙ x) ⊓ (K₁ ∙ x).orthogonalBilin B = ⊥ := by - rw [← Finset.coe_singleton] - refine eq_bot_iff.2 fun y h ↦ ?_ - obtain ⟨μ, -, rfl⟩ := Submodule.mem_span_finset.1 h.1 - replace h := h.2 x (by simp [Submodule.mem_span] : x ∈ Submodule.span K₁ ({x} : Finset V₁)) - rw [Finset.sum_singleton] at h ⊢ - suffices hμzero : μ x = 0 by rw [hμzero, zero_smul, Submodule.mem_bot] - rw [map_smulₛₗ] at h - exact Or.elim (smul_eq_zero.mp h) - (fun y ↦ by simpa using y) - (fun hfalse ↦ False.elim <| hx hfalse) - --- ↓ This lemma only applies in fields since we use the `mul_eq_zero` -theorem orthogonal_span_singleton_eq_to_lin_ker {B : V →ₗ[K] V →ₛₗ[J] V₂} (x : V) : - (K ∙ x).orthogonalBilin B = LinearMap.ker (B x) := by - ext y - simp_rw [Submodule.mem_orthogonalBilin_iff, LinearMap.mem_ker, Submodule.mem_span_singleton] - constructor - · exact fun h ↦ h x ⟨1, one_smul _ _⟩ - · rintro h _ ⟨z, rfl⟩ - rw [map_smulₛₗ₂, smul_eq_zero] - exact Or.intro_right _ h - --- todo: Generalize this to sesquilinear maps -theorem span_singleton_sup_orthogonal_eq_top {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : B x x ≠ 0) : - (K ∙ x) ⊔ (K ∙ x).orthogonalBilin B = ⊤ := by - rw [orthogonal_span_singleton_eq_to_lin_ker] - exact (B x).span_singleton_sup_ker_eq_top hx - --- todo: Generalize this to sesquilinear maps -/-- Given a bilinear form `B` and some `x` such that `B x x ≠ 0`, the span of the singleton of `x` - is complement to its orthogonal complement. -/ -theorem isCompl_span_singleton_orthogonal {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : B x x ≠ 0) : - IsCompl (K ∙ x) ((K ∙ x).orthogonalBilin B) := - { disjoint := disjoint_iff.2 <| span_singleton_inf_orthogonal_eq_bot B x hx - codisjoint := codisjoint_iff.2 <| span_singleton_sup_orthogonal_eq_top hx } - -end Orthogonal - /-! ### Adjoint pairs -/ section AdjointPair @@ -829,20 +734,6 @@ lemma IsSymm.nondegenerate_restrict_of_isCompl_ker {B : M →ₗ[R] M →ₗ[R] exact hx' u hu simpa [hW.inf_eq_bot] using! hx' -/-- The restriction of a reflexive bilinear map `B` onto a submodule `W` is -nondegenerate if `W` has trivial intersection with its orthogonal complement, -that is `Disjoint W (W.orthogonalBilin B)`. -/ -theorem nondegenerate_restrict_of_disjoint_orthogonal {B : M →ₗ[R] M →ₗ[R] M₁} (hB : B.IsRefl) - {W : Submodule R M} (hW : Disjoint W (W.orthogonalBilin B)) : - (B.domRestrict₁₂ W W).Nondegenerate := by - rw [(hB.domRestrict W).nondegenerate_iff_separatingLeft] - rintro ⟨x, hx⟩ b₁ - rw [Submodule.mk_eq_zero, ← Submodule.mem_bot R] - refine hW.le_bot ⟨hx, fun y hy ↦ ?_⟩ - specialize b₁ ⟨y, hy⟩ - simp_rw [domRestrict₁₂_apply] at b₁ - exact hB.eq_zero b₁ - end CommRing section IsOrthoᵢ diff --git a/Mathlib/LinearAlgebra/SesquilinearForm/Orthogonal.lean b/Mathlib/LinearAlgebra/SesquilinearForm/Orthogonal.lean new file mode 100644 index 00000000000000..958634cd279a05 --- /dev/null +++ b/Mathlib/LinearAlgebra/SesquilinearForm/Orthogonal.lean @@ -0,0 +1,140 @@ +/- +Copyright (c) 2022 Moritz Doll. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Moritz Doll, Christopher Hoskin +-/ +module + +public import Mathlib.LinearAlgebra.SesquilinearForm.Basic + +import Mathlib.Algebra.Module.Torsion.Field + +/-! +# Orthogonal complement + +This file defines the orthogonal submodule of a submodule with respect to a sesqui-blinear map. + +## Main declarations + +* `orthogonalBilin` provides the orthogonal complement with respect to a sesqui-bilinear map +-/ + +@[expose] public section + +open Module LinearMap + +variable {R R₁ R₂ M M₁ M₂ : Type*} + +namespace Submodule + +/-! ### The orthogonal complement -/ + +variable [CommSemiring R] [CommSemiring R₁] [CommSemiring R₂] +variable [AddCommMonoid M] [Module R M] +variable [AddCommMonoid M₁] [Module R₁ M₁] +variable [AddCommMonoid M₂] [Module R₂ M₂] +variable {I₁ : R₁ →+* R} {I₂ : R₂ →+* R} +variable {B : M₁ →ₛₗ[I₁] M₂ →ₛₗ[I₂] M} +variable {S T : Submodule R₁ M₁} + +variable (B S) in +/-- The orthogonal complement of a submodule `N` with respect to some bilinear map is the set of +elements `x` which are orthogonal to all elements of `N`; i.e., for all `y` in `N`, `B x y = 0`. + +Note that for general (neither symmetric nor antisymmetric) bilinear maps this definition has a +chirality; in addition to this "left" orthogonal complement one could define a "right" orthogonal +complement for which, for all `y` in `N`, `B y x = 0`. This variant definition is not currently +provided in mathlib. -/ +def orthogonalBilin : Submodule R₂ M₂ where + carrier := {y | ∀ x ∈ S, B x y = 0} + zero_mem' := by simp + add_mem' {u v} hu hv x hx := by simp [hu _ hx, hv _ hx] + smul_mem' c y hy x hx := by simp [hy _ hx] + +@[simp] theorem mem_orthogonalBilin_iff {m : M₂} : + m ∈ S.orthogonalBilin B ↔ ∀ n ∈ S, B n m = 0 := .rfl + +@[gcongr] theorem orthogonalBilin_le (h : S ≤ T) : + orthogonalBilin B T ≤ orthogonalBilin B S := fun _ hy _ hx ↦ hy _ (h hx) + +section IsRefl + +variable {I₂ : R₁ →+* R} {B : M₁ →ₛₗ[I₁] M₁ →ₛₗ[I₂] M} + +theorem le_orthogonalBilin_orthogonalBilin (b : B.IsRefl) : + S ≤ (S.orthogonalBilin B).orthogonalBilin B := fun n hn _m hm ↦ b _ _ (hm n hn) + +end IsRefl + +end Submodule + +namespace LinearMap + +section Orthogonal + +variable {K K₁ V V₁ V₂ : Type*} +variable [Field K] [AddCommGroup V] [Module K V] [Field K₁] [AddCommGroup V₁] [Module K₁ V₁] + [AddCommGroup V₂] [Module K V₂] {J : K →+* K} {J₁ : K₁ →+* K} {J₁' : K₁ →+* K} + +-- ↓ This lemma only applies in fields as we require `a * b = 0 → a = 0 ∨ b = 0` +theorem span_singleton_inf_orthogonal_eq_bot (B : V₁ →ₛₗ[J₁] V₁ →ₛₗ[J₁'] V₂) (x : V₁) + (hx : B x x ≠ 0) : (K₁ ∙ x) ⊓ (K₁ ∙ x).orthogonalBilin B = ⊥ := by + rw [← Finset.coe_singleton] + refine eq_bot_iff.2 fun y h ↦ ?_ + obtain ⟨μ, -, rfl⟩ := Submodule.mem_span_finset.1 h.1 + replace h := h.2 x (by simp [Submodule.mem_span] : x ∈ Submodule.span K₁ ({x} : Finset V₁)) + rw [Finset.sum_singleton] at h ⊢ + suffices hμzero : μ x = 0 by rw [hμzero, zero_smul, Submodule.mem_bot] + rw [map_smulₛₗ] at h + exact Or.elim (smul_eq_zero.mp h) + (fun y ↦ by simpa using y) + (fun hfalse ↦ False.elim <| hx hfalse) + +-- ↓ This lemma only applies in fields since we use the `mul_eq_zero` +theorem orthogonal_span_singleton_eq_to_lin_ker {B : V →ₗ[K] V →ₛₗ[J] V₂} (x : V) : + (K ∙ x).orthogonalBilin B = LinearMap.ker (B x) := by + ext y + simp_rw [Submodule.mem_orthogonalBilin_iff, LinearMap.mem_ker, Submodule.mem_span_singleton] + constructor + · exact fun h ↦ h x ⟨1, one_smul _ _⟩ + · rintro h _ ⟨z, rfl⟩ + rw [map_smulₛₗ₂, smul_eq_zero] + exact Or.intro_right _ h + +-- todo: Generalize this to sesquilinear maps +theorem span_singleton_sup_orthogonal_eq_top {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : B x x ≠ 0) : + (K ∙ x) ⊔ (K ∙ x).orthogonalBilin B = ⊤ := by + rw [orthogonal_span_singleton_eq_to_lin_ker] + exact (B x).span_singleton_sup_ker_eq_top hx + +-- todo: Generalize this to sesquilinear maps +/-- Given a bilinear form `B` and some `x` such that `B x x ≠ 0`, the span of the singleton of `x` + is complement to its orthogonal complement. -/ +theorem isCompl_span_singleton_orthogonal {B : V →ₗ[K] V →ₗ[K] K} {x : V} (hx : B x x ≠ 0) : + IsCompl (K ∙ x) ((K ∙ x).orthogonalBilin B) := + { disjoint := disjoint_iff.2 <| span_singleton_inf_orthogonal_eq_bot B x hx + codisjoint := codisjoint_iff.2 <| span_singleton_sup_orthogonal_eq_top hx } + +end Orthogonal + +section CommRing + +variable [CommRing R] [AddCommGroup M] [Module R M] [AddCommGroup M₁] [Module R M₁] {I I' : R →+* R} + +/-- The restriction of a reflexive bilinear map `B` onto a submodule `W` is +nondegenerate if `W` has trivial intersection with its orthogonal complement, +that is `Disjoint W (W.orthogonalBilin B)`. -/ +theorem nondegenerate_restrict_of_disjoint_orthogonal {B : M →ₗ[R] M →ₗ[R] M₁} (hB : B.IsRefl) + {W : Submodule R M} (hW : Disjoint W (W.orthogonalBilin B)) : + (B.domRestrict₁₂ W W).Nondegenerate := by + rw [(hB.domRestrict W).nondegenerate_iff_separatingLeft] + rintro ⟨x, hx⟩ b₁ + rw [Submodule.mk_eq_zero, ← Submodule.mem_bot R] + refine hW.le_bot ⟨hx, fun y hy ↦ ?_⟩ + specialize b₁ ⟨y, hy⟩ + simp_rw [domRestrict₁₂_apply] at b₁ + exact hB.eq_zero b₁ + +end CommRing + +end LinearMap diff --git a/Mathlib/LinearAlgebra/Span/Basic.lean b/Mathlib/LinearAlgebra/Span/Basic.lean index 8869671a304345..2f16a8eb65657b 100644 --- a/Mathlib/LinearAlgebra/Span/Basic.lean +++ b/Mathlib/LinearAlgebra/Span/Basic.lean @@ -529,6 +529,8 @@ end AddCommGroup section AddCommGroup +-- TODO: Multiple lemmas in this section should be in earlier files + variable [Semiring R] [Semiring R₂] variable [AddCommGroup M] [Module R M] [AddCommGroup M₂] [Module R₂ M₂] variable {τ₁₂ : R →+* R₂} [RingHomSurjective τ₁₂] @@ -539,6 +541,11 @@ theorem comap_map_eq (f : M →ₛₗ[τ₁₂] M₂) (p : Submodule R M) : rintro x ⟨y, hy, e⟩ exact mem_sup.2 ⟨y, hy, x - y, by simpa using sub_eq_zero.2 e.symm, by simp⟩ +theorem map_eq_range_iff {f : M →ₛₗ[τ₁₂] M₂} {p : Submodule R M} : + map f p = f.range ↔ Codisjoint p f.ker := by + simp_rw [le_antisymm_iff, LinearMap.map_le_range, true_and, ← map_top, map_le_iff_le_comap, + comap_map_eq, codisjoint_iff_le_sup] + theorem map_lt_map_of_le_of_sup_lt_sup {p p' : Submodule R M} {f : M →ₛₗ[τ₁₂] M₂} (hab : p ≤ p') (h : p ⊔ LinearMap.ker f < p' ⊔ LinearMap.ker f) : Submodule.map f p < Submodule.map f p' := by simp_rw [← comap_map_eq] at h @@ -587,28 +594,16 @@ lemma comap_covBy_of_surjective {f : M →ₛₗ[τ₁₂] M₂} (hf : Surjectiv rwa [← comap_lt_comap_iff_of_surjective hf, comap_map_eq, sup_eq_left.mpr] refine (LinearMap.ker_le_comap (f : M →ₛₗ[τ₁₂] M₂)).trans h₁.le +@[deprecated map_eq_range_iff (since := "2026-07-01")] lemma _root_.LinearMap.range_domRestrict_eq_range_iff {f : M →ₛₗ[τ₁₂] M₂} {S : Submodule R M} : - LinearMap.range (f.domRestrict S) = LinearMap.range f ↔ S ⊔ (LinearMap.ker f) = ⊤ := by - refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ - · rw [eq_top_iff] - intro x _ - have : f x ∈ LinearMap.range f := LinearMap.mem_range_self f x - rw [← h] at this - obtain ⟨y, hy⟩ : ∃ y : S, f.domRestrict S y = f x := this - have : (y : M) + (x - y) ∈ S ⊔ (LinearMap.ker f) := Submodule.add_mem_sup y.2 (by simp [← hy]) - simpa using this - · refine le_antisymm (LinearMap.range_domRestrict_le_range f S) ?_ - rintro x ⟨y, rfl⟩ - obtain ⟨s, hs, t, ht, rfl⟩ : ∃ s, s ∈ S ∧ ∃ t, t ∈ LinearMap.ker f ∧ s + t = y := - Submodule.mem_sup.1 (by simp [h]) - exact ⟨⟨s, hs⟩, by simp [LinearMap.mem_ker.1 ht]⟩ + LinearMap.range (f.domRestrict S) = LinearMap.range f ↔ Codisjoint S f.ker := by + simp [map_eq_range_iff] @[simp] lemma _root_.LinearMap.surjective_domRestrict_iff {f : M →ₛₗ[τ₁₂] M₂} {S : Submodule R M} (hf : Surjective f) : - Surjective (f.domRestrict S) ↔ S ⊔ LinearMap.ker f = ⊤ := by + Surjective (f.domRestrict S) ↔ Codisjoint S f.ker := by rw [← LinearMap.range_eq_top] at hf ⊢ - rw [← hf] - exact LinearMap.range_domRestrict_eq_range_iff + rw [← hf, LinearMap.range_domRestrict, map_eq_range_iff] lemma biSup_comap_eq_top_of_surjective {ι : Type*} (s : Set ι) (hs : s.Nonempty) (p : ι → Submodule R₂ M₂) (hp : ⨆ i ∈ s, p i = ⊤) diff --git a/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean b/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean index 7ccbac331f11e5..4a1147d6b0fbc8 100644 --- a/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean +++ b/Mathlib/LinearAlgebra/SymmetricAlgebra/Basic.lean @@ -40,19 +40,21 @@ quotiented out by. -/ inductive TensorAlgebra.SymRel : TensorAlgebra R M → TensorAlgebra R M → Prop where | mul_comm (x y : M) : SymRel (ι R x * ι R y) (ι R y * ι R x) +/-- `SymRel` as a ring congruence, used to build the quotient. -/ +@[no_expose] def TensorAlgebra.symRingCon : RingCon (TensorAlgebra R M) := ringConGen (SymRel R M) + open TensorAlgebra /-- Concrete construction of the symmetric algebra of `M` by quotienting out the tensor algebra by the commutativity relation. -/ -abbrev SymmetricAlgebra := RingQuot (SymRel R M) +abbrev SymmetricAlgebra := symRingCon R M |>.Quotient namespace SymmetricAlgebra /-- Algebra homomorphism from the tensor algebra over `M` to the symmetric algebra over `M`. -/ -abbrev algHom : TensorAlgebra R M →ₐ[R] SymmetricAlgebra R M := RingQuot.mkAlgHom R (SymRel R M) +abbrev algHom : TensorAlgebra R M →ₐ[R] SymmetricAlgebra R M := RingCon.mkₐ R _ -lemma algHom_surjective : Function.Surjective (algHom R M) := - RingQuot.mkAlgHom_surjective _ _ +lemma algHom_surjective : Function.Surjective (algHom R M) := Quotient.mk_surjective /-- Canonical inclusion of `M` into the symmetric algebra `SymmetricAlgebra R M`. -/ def ι : M →ₗ[R] SymmetricAlgebra R M := algHom R M ∘ₗ TensorAlgebra.ι R @@ -78,7 +80,9 @@ instance : CommSemiring (SymmetricAlgebra R M) where | algebraMap r => exact Algebra.commute_algebraMap_right _ _ | ι x => induction a using SymmetricAlgebra.induction with | algebraMap r => exact Algebra.commute_algebraMap_left _ _ - | ι y => simp [commute_iff_eq, ι, ← map_mul, RingQuot.mkAlgHom_rel _ (SymRel.mul_comm x y)] + | ι y => + have := RingCon.le_ringConGen (r := SymRel R M) _ _ <| SymRel.mul_comm y x + simpa [commute_iff_eq, ι, ← RingCon.coe_mul] | mul a b ha hb => exact ha.mul_left hb | add a b ha hb => exact ha.add_left hb | mul b c hb hc => exact hb.mul_right hc @@ -86,29 +90,32 @@ instance : CommSemiring (SymmetricAlgebra R M) where instance (R M) [CommRing R] [AddCommMonoid M] [Module R M] : CommRing (SymmetricAlgebra R M) where __ := (inferInstance : CommSemiring (SymmetricAlgebra R M)) - __ := (inferInstance : Ring (RingQuot (SymRel R M))) + __ := (inferInstance : Ring (SymmetricAlgebra R M)) variable {R M} {A : Type*} [CommSemiring A] [Algebra R A] /-- For any linear map `f : M →ₗ[R] A`, `SymmetricAlgebra.lift f` lifts the linear map to an R-algebra homomorphism from `SymmetricAlgebra R M` to `A`. -/ -def lift : (M →ₗ[R] A) ≃ (SymmetricAlgebra R M →ₐ[R] A) := by +def lift : (M →ₗ[R] A) ≃ (SymmetricAlgebra R M →ₐ[R] A) := let equiv : (TensorAlgebra R M →ₐ[R] A) ≃ - {f : TensorAlgebra R M →ₐ[R] A // ∀ {x y}, (TensorAlgebra.SymRel R M) x y → f x = f y} := by - refine (Equiv.subtypeUnivEquiv fun h _ _ h' ↦ ?_).symm - induction h' with | mul_comm x y => rw [map_mul, map_mul, mul_comm] - exact (TensorAlgebra.lift R).trans <| equiv.trans <| RingQuot.liftAlgHom R + {f : TensorAlgebra R M →ₐ[R] A // TensorAlgebra.symRingCon R M ≤ RingCon.ker f.toRingHom} := + (Equiv.subtypeUnivEquiv fun h _ _ h' ↦ ?_).symm + (TensorAlgebra.lift R).trans <| equiv.trans <| RingCon.liftₐEquiv (symRingCon R M) +where finally + refine RingCon.ringConGen_le.2 (fun x y h' => ?_) h' + induction h' with | mul_comm x y + rw [RingCon.ker_apply, map_mul, map_mul, mul_comm] variable (f : M →ₗ[R] A) @[simp] lemma lift_ι_apply (a : M) : lift f (ι R M a) = f a := by - simp [lift, ι, algHom] + simp [lift, ι, algHom, RingCon.liftₐEquiv] @[simp] lemma lift_comp_ι : lift f ∘ₗ ι R M = f := LinearMap.ext <| lift_ι_apply f -@[ext 1100] +@[ext 1200] theorem algHom_ext {F G : SymmetricAlgebra R M →ₐ[R] A} (h : F ∘ₗ ι R M = (G ∘ₗ ι R M : M →ₗ[R] A)) : F = G := by ext x diff --git a/Mathlib/LinearAlgebra/SymplecticGroup.lean b/Mathlib/LinearAlgebra/SymplecticGroup.lean index f45df9b46ce4d0..c5ae74d50bf355 100644 --- a/Mathlib/LinearAlgebra/SymplecticGroup.lean +++ b/Mathlib/LinearAlgebra/SymplecticGroup.lean @@ -39,6 +39,13 @@ section JMatrixLemmas def J : Matrix (l ⊕ l) (l ⊕ l) R := Matrix.fromBlocks 0 (-1) 1 0 +variable {R} in +@[simp] +theorem map_J {F S : Type*} [CommRing S] [FunLike F R S] + [AddMonoidHomClass F R S] [OneHomClass F R S] (f : F) : + (J l R).map f = J l S := by + simp [J, fromBlocks_map, Matrix.map_neg] + @[simp] theorem J_transpose : (J l R)ᵀ = -J l R := by rw [J, fromBlocks_transpose, ← neg_one_smul R (fromBlocks _ _ _ _ : Matrix (l ⊕ l) (l ⊕ l) R), @@ -129,6 +136,10 @@ theorem symplectic_det (hA : A ∈ symplecticGroup l R) : IsUnit <| det A := by rw [mul_comm A.det, mul_assoc] at hA exact hA +theorem map_mem {F S : Type*} [CommRing S] [FunLike F R S] [RingHomClass F R S] + (hA : A ∈ symplecticGroup l R) (f : F) : A.map f ∈ symplecticGroup l S := by + simp_rw [mem_iff, ← transpose_map, ← map_J _ f, ← Matrix.map_mul, mem_iff.mp hA] + theorem transpose_mem (hA : A ∈ symplecticGroup l R) : Aᵀ ∈ symplecticGroup l R := by rw [mem_iff] at hA ⊢ rw [transpose_transpose] diff --git a/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean b/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean index a8e63217d9d82c..704c34f0748b31 100644 --- a/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean +++ b/Mathlib/LinearAlgebra/TensorAlgebra/Basic.lean @@ -6,10 +6,10 @@ Authors: Adam Topaz module public import Mathlib.Algebra.FreeAlgebra -public import Mathlib.Algebra.RingQuot public import Mathlib.Algebra.TrivSqZeroExt.Basic public import Mathlib.Algebra.Algebra.Operations public import Mathlib.LinearAlgebra.Multilinear.Basic +public import Mathlib.RingTheory.Congruence.Hom /-! # Tensor Algebras @@ -56,12 +56,14 @@ inductive Rel : FreeAlgebra R M → FreeAlgebra R M → Prop | smul {r : R} {a : M} : Rel (FreeAlgebra.ι R (r • a)) (algebraMap R (FreeAlgebra R M) r * FreeAlgebra.ι R a) +/-- `Rel` as a ring congruence, used to build the quotient. -/ +@[no_expose] def ringCon : RingCon (FreeAlgebra R M) := ringConGen (Rel R M) + end TensorAlgebra /-- The tensor algebra of the module `M` over the commutative semiring `R`. -/ -def TensorAlgebra := - RingQuot (TensorAlgebra.Rel R M) +def TensorAlgebra := TensorAlgebra.ringCon R M |>.Quotient deriving Inhabited, Semiring -- `IsScalarTower` is not needed, but the instance isn't really canonical without it. @@ -70,7 +72,7 @@ instance instAlgebra {R A M} [CommSemiring R] [AddCommMonoid M] [CommSemiring A] [Algebra R A] [Module R M] [Module A M] [IsScalarTower R A M] : Algebra R (TensorAlgebra A M) := - inferInstanceAs <| Algebra R (RingQuot _) + inferInstanceAs <| Algebra R (RingCon.Quotient _) -- verify there is no diamond -- but doesn't work at `reducible_and_instances` https://github.com/leanprover-community/mathlib4/issues/10906 @@ -80,18 +82,18 @@ instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommMonoid M] [CommSemi [Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M] [IsScalarTower R A M] [IsScalarTower S A M] : SMulCommClass R S (TensorAlgebra A M) := - inferInstanceAs <| SMulCommClass R S (RingQuot _) + inferInstanceAs <| SMulCommClass R S (RingCon.Quotient _) instance {R S A M} [CommSemiring R] [CommSemiring S] [AddCommMonoid M] [CommSemiring A] [SMul R S] [Algebra R A] [Algebra S A] [Module R M] [Module S M] [Module A M] [IsScalarTower R A M] [IsScalarTower S A M] [IsScalarTower R S A] : IsScalarTower R S (TensorAlgebra A M) := - inferInstanceAs <| IsScalarTower R S (RingQuot _) + inferInstanceAs <| IsScalarTower R S (RingCon.Quotient _) namespace TensorAlgebra instance {S : Type*} [CommRing S] [Module S M] : Ring (TensorAlgebra S M) := - inferInstanceAs <| Ring (RingQuot _) + inferInstanceAs <| Ring (RingCon.Quotient _) -- verify there is no diamond -- but doesn't work at `reducible_and_instances` https://github.com/leanprover-community/mathlib4/issues/10906 @@ -104,16 +106,16 @@ set_option backward.isDefEq.respectTransparency false in /-- The canonical linear map `M →ₗ[R] TensorAlgebra R M`. -/ irreducible_def ι : M →ₗ[R] TensorAlgebra R M := - { toFun := fun m => RingQuot.mkAlgHom R _ (FreeAlgebra.ι R m) + { toFun := fun m => RingCon.toQuotient (FreeAlgebra.ι R m) map_add' := fun x y => by - rw [← map_add (RingQuot.mkAlgHom R (Rel R M))] - exact RingQuot.mkAlgHom_rel R Rel.add + rw [← RingCon.coe_add] + exact Quotient.sound <| RingConGen.Rel.of _ _ Rel.add map_smul' := fun r x => by - rw [← map_smul (RingQuot.mkAlgHom R (Rel R M))] - exact RingQuot.mkAlgHom_rel R Rel.smul } + rw [← RingCon.coe_smul] + exact Quotient.sound <| RingConGen.Rel.of _ _ <| Rel.smul} theorem ringQuot_mkAlgHom_freeAlgebra_ι_eq_ι (m : M) : - RingQuot.mkAlgHom R (Rel R M) (FreeAlgebra.ι R m) = ι R m := by + RingCon.mkₐ R (ringCon R M) (FreeAlgebra.ι R m) = ι R m := by rw [ι] rfl @@ -122,24 +124,26 @@ of `f` to a morphism of `R`-algebras `TensorAlgebra R M → A`. -/ @[simps symm_apply] def lift {A : Type*} [Semiring A] [Algebra R A] : (M →ₗ[R] A) ≃ (TensorAlgebra R M →ₐ[R] A) := - { toFun := - RingQuot.liftAlgHom R ∘ fun f => - ⟨FreeAlgebra.lift R (⇑f), fun x y (h : Rel R M x y) => by - induction h <;> - simp only [Algebra.smul_def, FreeAlgebra.lift_ι_apply, map_smulₛₗ, RingHom.id_apply, - map_mul, AlgHom.commutes, map_add]⟩ + { toFun f := + RingCon.liftₐ (ringCon R M) (FreeAlgebra.lift R (f)) <| by + grw [ringCon, RingCon.ringConGen_le] + intro x y h + induction h <;> + simp [Algebra.smul_def, FreeAlgebra.lift_ι_apply, + map_mul, AlgHom.commutes, map_add, RingCon.ker] invFun := fun F => F.toLinearMap.comp (ι R) left_inv := fun f => by rw [ι] ext1 x - exact (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (FreeAlgebra.lift_ι_apply f x) + dsimp + exact (RingCon.liftₐ_mk _ _ _ _).trans (FreeAlgebra.lift_ι_apply f x) right_inv := fun F => - RingQuot.ringQuot_ext' _ _ _ <| + RingCon.Quotient.hom_extₐ <| FreeAlgebra.hom_ext <| funext fun x => by rw [ι] - exact - (RingQuot.liftAlgHom_mkAlgHom_apply _ _ _ _).trans (FreeAlgebra.lift_ι_apply _ _) } + simp + rfl } variable {R} diff --git a/Mathlib/LinearAlgebra/TensorProduct/Basic.lean b/Mathlib/LinearAlgebra/TensorProduct/Basic.lean index e7526ce6d6d11e..4784cba63d32aa 100644 --- a/Mathlib/LinearAlgebra/TensorProduct/Basic.lean +++ b/Mathlib/LinearAlgebra/TensorProduct/Basic.lean @@ -393,7 +393,6 @@ protected theorem neg_add_cancel (x : M ⊗[R] N) : -x + x = 0 := instance addCommGroup : AddCommGroup (M ⊗[R] N) where neg_add_cancel := fun x => TensorProduct.neg_add_cancel x - zsmul := (· • ·) zsmul_zero' := by simp zsmul_succ' := by simp [add_comm, TensorProduct.add_smul] zsmul_neg' := fun n x => by diff --git a/Mathlib/LinearAlgebra/TensorProduct/Defs.lean b/Mathlib/LinearAlgebra/TensorProduct/Defs.lean index 528e297c60fed3..0a0180c5d21455 100644 --- a/Mathlib/LinearAlgebra/TensorProduct/Defs.lean +++ b/Mathlib/LinearAlgebra/TensorProduct/Defs.lean @@ -224,7 +224,7 @@ Note that in the special case that `R = R'`, since `R` is commutative, we just g action on a tensor product of two modules. This special case is important enough that, for performance reasons, we define it explicitly below. -/ instance leftHasSMul : SMul R' (M ⊗[R] N) := - ⟨fun r => + id ⟨fun r => (addConGen (TensorProduct.Eqv R M N)).lift (SMul.aux r : _ →+ M ⊗[R] N) <| AddCon.addConGen_le.2 fun x y hxy => match x, y, hxy with @@ -270,10 +270,8 @@ protected theorem add_smul (r s : R'') (x : M ⊗[R] N) : (r + s) • x = r • rw [ihx, ihy, add_add_add_comm] instance addMonoid : AddMonoid (M ⊗[R] N) where - nsmul := fun n v => n • v nsmul_zero := by simp [TensorProduct.zero_smul] - nsmul_succ := by simp only [TensorProduct.one_smul, TensorProduct.add_smul, add_comm, - forall_const] + nsmul_succ := by simp only [TensorProduct.one_smul, TensorProduct.add_smul, forall_const] instance addCommMonoid : AddCommMonoid (M ⊗[R] N) where diff --git a/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean b/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean index 7ec7f4c133f8f9..174859ad12e7f1 100644 --- a/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean +++ b/Mathlib/LinearAlgebra/TensorProduct/Subalgebra.lean @@ -248,7 +248,7 @@ variable {A B} in theorem val_mulMap'_tmul (a : A) (b : B) : (mulMap' A B (a ⊗ₜ[R] b) : S) = a.1 * b.1 := rfl theorem mulMap'_surjective : Function.Surjective (mulMap' A B) := by - simp_rw [mulMap', AlgHom.coe_comp, AlgEquiv.coe_algHom, + simp_rw [mulMap', AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, EquivLike.comp_surjective, AlgHom.rangeRestrict_surjective] end Subalgebra diff --git a/Mathlib/LinearAlgebra/Trace.lean b/Mathlib/LinearAlgebra/Trace.lean index ee5b930430840d..7eaacb4b1b6dc9 100644 --- a/Mathlib/LinearAlgebra/Trace.lean +++ b/Mathlib/LinearAlgebra/Trace.lean @@ -12,6 +12,7 @@ public import Mathlib.RingTheory.TensorProduct.Finite public import Mathlib.RingTheory.TensorProduct.Free import Mathlib.LinearAlgebra.GeneralLinearGroup.AlgEquiv +import Mathlib.RingTheory.SimpleRing.Matrix /-! # Trace of a linear map @@ -325,11 +326,12 @@ theorem trace_conj' (f : M →ₗ[R] M) (e : M ≃ₗ[R] N) : trace R N (e.conj LinearMap.trace_map ((Matrix.toLinAlgEquiv'.symm.trans (AlgEquivClass.toAlgEquiv f)).trans Matrix.toLinAlgEquiv') x.toLin' --- TODO: show `(f x).trace = x.trace` for when `f : Matrix m m K →ₐ[K] Matrix m m K` --- (using Skolem-Noether) -proof_wanted _root_.Matrix.trace_map' {K m F : Type*} [Field K] [Fintype m] [DecidableEq m] +@[simp] theorem _root_.Matrix.trace_map' {K m F : Type*} [Field K] [Fintype m] [DecidableEq m] [FunLike F (Matrix m m K) (Matrix m m K)] [AlgHomClass F K _ _] (f : F) (x : Matrix m m K) : - (f x).trace = x.trace + (f x).trace = x.trace := by + by_cases! Nonempty m + · exact Matrix.trace_map (AlgEquiv.ofBijective _ (AlgHomClass.toAlgHom f).bijective) x + · simp theorem IsProj.trace {p : Submodule R M} {f : M →ₗ[R] M} (h : IsProj p f) [Module.Free R p] [Module.Finite R p] [Module.Free R (ker f)] [Module.Finite R (ker f)] : diff --git a/Mathlib/Logic/Basic.lean b/Mathlib/Logic/Basic.lean index b20343ec1ec3f1..e1f659e30a1de0 100644 --- a/Mathlib/Logic/Basic.lean +++ b/Mathlib/Logic/Basic.lean @@ -734,7 +734,7 @@ lemma eq_true_intro {a : Prop} (h : a) : a = True := propext (iff_true_intro h) lemma eq_false_intro {a : Prop} (h : ¬a) : a = False := propext (iff_false_intro h) -- FIXME: `alias` creates `def Iff.eq := propext` instead of `lemma Iff.eq := propext` -@[nolint defLemma] alias Iff.eq := propext +alias Iff.eq := propext lemma iff_eq_eq {a b : Prop} : (a ↔ b) = (a = b) := propext ⟨propext, Eq.to_iff⟩ diff --git a/Mathlib/Logic/Equiv/Fin/Basic.lean b/Mathlib/Logic/Equiv/Fin/Basic.lean index 341c95b6f54503..5bea2e2b9b043e 100644 --- a/Mathlib/Logic/Equiv/Fin/Basic.lean +++ b/Mathlib/Logic/Equiv/Fin/Basic.lean @@ -126,6 +126,9 @@ theorem finSuccEquiv_zero : (finSuccEquiv n) 0 = none := theorem finSuccEquiv_succ (m : Fin n) : (finSuccEquiv n) m.succ = some m := finSuccEquiv'_above (Fin.zero_le _) +@[simp] +theorem finSuccEquiv_last (n : ℕ) : finSuccEquiv (n + 1) (Fin.last (n + 1)) = Fin.last n := rfl + @[simp] theorem finSuccEquiv_symm_none : (finSuccEquiv n).symm none = 0 := finSuccEquiv'_symm_none _ diff --git a/Mathlib/Logic/Equiv/Set.lean b/Mathlib/Logic/Equiv/Set.lean index c1a73bf4892d9d..b430f0e1e4d1cf 100644 --- a/Mathlib/Logic/Equiv/Set.lean +++ b/Mathlib/Logic/Equiv/Set.lean @@ -249,6 +249,7 @@ protected def singleton {α} (a : α) : ({a} : Set α) ≃ PUnit.{u} := lemma _root_.Equiv.strictMono_setCongr {α : Type*} [Preorder α] {S T : Set α} (h : S = T) : StrictMono (setCongr h) := fun _ _ ↦ id +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias Equiv.strictMono_setCongr := Equiv.strictMono_setCongr /-- If `a ∉ s`, then `insert a s` is equivalent to `s ⊕ PUnit`. -/ diff --git a/Mathlib/Logic/Function/Basic.lean b/Mathlib/Logic/Function/Basic.lean index 6ab2a0cf0c7f63..d2d3f67f4f10da 100644 --- a/Mathlib/Logic/Function/Basic.lean +++ b/Mathlib/Logic/Function/Basic.lean @@ -352,6 +352,10 @@ theorem Bijective.of_comp_iff' {f : α → β} (hf : Bijective f) (g : γ → α Function.Bijective (f ∘ g) ↔ Function.Bijective g := and_congr (Injective.of_comp_iff hf.injective _) (Surjective.of_comp_iff' hf _) +theorem Bijective.of_comp_left {f : α → β} {g : γ → α} (hfg : Function.Bijective (f ∘ g)) + (hf : Function.Injective f) : Function.Bijective g := + ⟨hfg.1.of_comp, hfg.2.of_comp_left hf⟩ + /-- If `f : α → α → β` is surjective, then every endofunction on `β` has a fixed point. This is an instance of Lawvere's fixed-point theorem applied to the category of types and functions. It is the diagonal argument underlying `cantor_surjective` and diff --git a/Mathlib/Logic/Function/Defs.lean b/Mathlib/Logic/Function/Defs.lean index 04b9b63554da8d..0c3098fed6abe8 100644 --- a/Mathlib/Logic/Function/Defs.lean +++ b/Mathlib/Logic/Function/Defs.lean @@ -31,18 +31,75 @@ def dcomp {β : α → Sort u₂} {φ : ∀ {x : α}, β x → Sort u₃} (f : @[inherit_doc] infixr:80 " ∘' " => Function.dcomp +section DComp + +variable {ι} {β : ι → Sort*} {φ : ∀ {i : ι}, β i → Sort*} (f : ∀ {i : ι} (y : β i), φ y) + (g : ∀ i, β i) (i : ι) + +theorem dcomp_def : @f ∘' g = fun i => f (g i) := rfl + +theorem dcomp_apply : dcomp @f g i = f (g i) := rfl + +@[simp] theorem dcomp_eq_comp {α β γ} (f : β → γ) (g : α → β) : f ∘' g = f ∘ g := rfl + +end DComp + /-- Product of functions: `Function.prod f g i = (f i, g i)`, where the types of `f i` and `g i` may depend on `i`. -/ protected def prod {ι} {α β : ι → Type*} (f : ∀ i, α i) (g : ∀ i, β i) (i : ι) : α i × β i := (f i, g i) -@[simp] lemma prod_apply {ι} {α β : ι → Type*} (f : ∀ i, α i) (g : ∀ i, β i) (i : ι) : - Function.prod f g i = (f i , g i) := rfl +section DProd -lemma prod_fst_snd {α β} : Function.prod (Prod.fst : α × β → α) (Prod.snd : α × β → β) = id := - rfl -lemma prod_snd_fst {α β} : Function.prod (Prod.snd : α × β → β) (Prod.fst : α × β → α) = .swap := - rfl +variable {ι} {α β : ι → Type*} (f f' : ∀ i, α i) (g g' : ∀ i, β i) + +theorem prod_def : Function.prod f g = fun i : ι => (f i, g i) := rfl + +@[simp, grind =] lemma prod_apply (i : ι) : Function.prod f g i = (f i, g i) := rfl + +variable {f f' g g'} in +@[simp] theorem prod_inj : Function.prod f g = Function.prod f' g' ↔ f = f' ∧ g = g' := by + simp [funext_iff, Prod.ext_iff, forall_and] + +end DProd + +section Prod + +variable {α β : Type*} {ι : Sort*} (f : ι → α) (g : ι → β) + +theorem prod_ext_iff {h h' : ι → α × β} : + h = h' ↔ Prod.fst ∘ h = Prod.fst ∘ h' ∧ Prod.snd ∘ h = Prod.snd ∘ h' := + prod_inj + +@[simp] lemma prod_fst_snd : Function.prod (Prod.fst : _ → α) (Prod.snd : _ → β) = id := rfl +@[simp] lemma prod_snd_fst : Function.prod (Prod.snd : _ → β) (Prod.fst : _ → α) = .swap := rfl + +@[simp] theorem fst_comp_prod : Prod.fst ∘ Function.prod f g = f := rfl +@[simp] theorem snd_comp_prod : Prod.snd ∘ Function.prod f g = g := rfl + +@[simp] theorem prod_fst_comp_snd_comp (h : ι → α × β) : + Function.prod (Prod.fst ∘ h) (Prod.snd ∘ h) = h := rfl + +theorem const_prod (p : α × β) : const ι p = Function.prod (const ι p.1) (const ι p.2) := rfl + +@[simp] theorem prod_const_const (a : α) (b : β) : + Function.prod (const ι a) (const ι b) = const ι (a, b) := rfl + +theorem prod_comp {κ} (h : κ → ι) : Function.prod f g ∘ h = Function.prod (f ∘ h) (g ∘ h) := rfl + +@[simp] theorem prod_comp_fst_comp_snd {α₁ α₂ β₁ β₂} (f : α₁ → α₂) (g : β₁ → β₂) : + Function.prod (f ∘ Prod.fst) (g ∘ Prod.snd) = Prod.map f g := rfl + +@[simp] theorem map_comp_prod {γ δ} (h : α → γ) (k : β → δ) : + Prod.map h k ∘ Function.prod f g = Function.prod (h ∘ f) (k ∘ g) := rfl + +theorem prod_comp_prod {γ δ} (h : α × β → γ) (k : α × β → δ) : + Function.prod h k ∘ Function.prod f g = + Function.prod (h ∘ Function.prod f g) (k ∘ Function.prod f g) := rfl + +@[simp] theorem swap_comp_prod : Prod.swap ∘ Function.prod f g = Function.prod g f := rfl + +end Prod /-- Given functions `f : β → β → φ` and `g : α → β`, produce a function `α → α → φ` that evaluates `g` on each argument, then applies `f` to the results. Can be used, e.g., to transfer a relation diff --git a/Mathlib/Logic/Lemmas.lean b/Mathlib/Logic/Lemmas.lean index 0266bda809af21..0957d6c7208bcd 100644 --- a/Mathlib/Logic/Lemmas.lean +++ b/Mathlib/Logic/Lemmas.lean @@ -12,6 +12,7 @@ public import Mathlib.Tactic.Tauto /-! # More basic logic properties + A few more logic lemmas. These are in their own file, rather than `Logic.Basic`, because it is convenient to be able to use the `tauto` or `split_ifs` tactics. diff --git a/Mathlib/Logic/Relation.lean b/Mathlib/Logic/Relation.lean index 8607a7d2f4a46c..953a9a444d0101 100644 --- a/Mathlib/Logic/Relation.lean +++ b/Mathlib/Logic/Relation.lean @@ -9,6 +9,7 @@ public import Mathlib.Logic.Relator public import Mathlib.Tactic.Use public import Mathlib.Tactic.MkIffOfInductiveProp public import Mathlib.Tactic.SimpRw +public import Mathlib.Order.Defs.Prop public import Mathlib.Order.Defs.Unbundled public import Batteries.Logic public import Batteries.Tactic.Trans @@ -53,7 +54,7 @@ the bundled version, see `Rel`. open Function -variable {α β γ δ ε ζ : Sort*} +variable {α β γ δ ε ζ : Type*} section NeImp @@ -76,19 +77,21 @@ theorem Std.Refl.ne_imp_iff [Std.Refl r] {x y : α} : x ≠ y → r x y ↔ r x ⟨Std.Refl.rel_of_ne_imp, fun hr _ ↦ hr⟩ @[deprecated (since := "2026-03-27")] alias Reflexive.ne_imp_iff := Std.Refl.ne_imp_iff - @[deprecated (since := "2026-03-27")] alias reflexive_ne_imp_iff := Std.Refl.ne_imp_iff -theorem refl_iff_subrelation_eq : Std.Refl r ↔ Subrelation Eq r := by - grind [Std.Refl, Subrelation] +theorem refl_iff_eq_le : Std.Refl r ↔ Eq ≤ r := by + unfold Pi.hasLe Prop.le + grind [Std.Refl] -@[deprecated (since := "2026-03-27")] alias reflexive_iff_subrelation_eq := refl_iff_subrelation_eq +@[deprecated (since := "2026-06-30")] alias refl_iff_subrelation_eq := refl_iff_eq_le +@[deprecated (since := "2026-03-27")] alias reflexive_iff_subrelation_eq := refl_iff_eq_le -theorem irrefl_iff_subrelation_ne : Std.Irrefl r ↔ Subrelation r Ne := by - grind [Std.Irrefl, Subrelation] +theorem irrefl_iff_le_ne : Std.Irrefl r ↔ r ≤ Ne := by + unfold Pi.hasLe Prop.le + grind [Std.Irrefl] -@[deprecated (since := "2026-02-12")] -alias irreflexive_iff_subrelation_ne := irrefl_iff_subrelation_ne +@[deprecated (since := "2026-06-30")] alias irrefl_iff_subrelation_ne := irrefl_iff_le_ne +@[deprecated (since := "2026-02-12")] alias irreflexive_iff_subrelation_ne := irrefl_iff_le_ne protected theorem Std.Symm.iff [Std.Symm r] (x y : α) : r x y ↔ r y x := ⟨symm_of r, symm_of r⟩ @@ -280,13 +283,12 @@ lemma map_equivalence {r : α → α → Prop} (hr : Equivalence r) (f : α → symm := @(hr.stdSymm.map f |>.symm) trans := @(hr.isTrans.map hf_ker |>.trans) --- TODO: state this using `≤`, after adjusting imports. -lemma map_mono {r s : α → β → Prop} {f : α → γ} {g : β → δ} (h : ∀ x y, r x y → s x y) : - ∀ x y, Relation.Map r f g x y → Relation.Map s f g x y := +lemma map_mono {r s : α → β → Prop} {f : α → γ} {g : β → δ} (h : r ≤ s) : + Relation.Map r f g ≤ Relation.Map s f g := fun _ _ ⟨x, y, hxy, hx, hy⟩ => ⟨x, y, h _ _ hxy, hx, hy⟩ -lemma le_onFun_map {r : α → α → Prop} (f : α → β) : Subrelation r (Relation.Map r f f on f) := by - intro +lemma le_onFun_map {r : α → α → Prop} (f : α → β) : r ≤ (Relation.Map r f f on f) := by + unfold Pi.hasLe Prop.le grind [Relation.Map] lemma onFun_map_eq_of_injective {r : α → α → Prop} {f : α → β} (hinj : f.Injective) : @@ -294,8 +296,8 @@ lemma onFun_map_eq_of_injective {r : α → α → Prop} {f : α → β} (hinj : ext x y exact ⟨fun ⟨x', y', hr, hx, hy⟩ ↦ hinj hx ▸ hinj hy ▸ hr, fun h ↦ ⟨x, y, h, rfl, rfl⟩⟩ -lemma map_onFun_le {r : β → β → Prop} (f : α → β) : Subrelation (Relation.Map (r on f) f f) r := by - intro +lemma map_onFun_le {r : β → β → Prop} (f : α → β) : Relation.Map (r on f) f f ≤ r := by + unfold Pi.hasLe Prop.le grind [Relation.Map] lemma map_onFun_eq_of_surjective {r : β → β → Prop} {f : α → β} (hsurj : f.Surjective) : @@ -355,13 +357,16 @@ inductive EqvGen : α → α → Prop attribute [mk_iff] TransGen attribute [grind] TransGen +theorem reflGen_le_reflTransGen : ReflGen r ≤ ReflTransGen r + | a, _, .refl => by rfl + | _, _, .single h => ReflTransGen.tail ReflTransGen.refl h + namespace ReflGen -theorem to_reflTransGen : ∀ {a b}, ReflGen r a b → ReflTransGen r a b - | a, _, refl => by rfl - | _, _, single h => ReflTransGen.tail ReflTransGen.refl h +theorem to_reflTransGen {a b} : ReflGen r a b → ReflTransGen r a b := + reflGen_le_reflTransGen a b -theorem mono {p : α → α → Prop} (hp : ∀ a b, r a b → p a b) : ∀ {a b}, ReflGen r a b → ReflGen p a b +theorem mono {p : α → α → Prop} (hp : r ≤ p) : ReflGen r ≤ ReflGen p | a, _, ReflGen.refl => by rfl | a, b, single h => single (hp a b h) @@ -436,6 +441,9 @@ theorem trans (hab : ReflTransGen r a b) (hbc : ReflTransGen r b c) : ReflTransG theorem single (hab : r a b) : ReflTransGen r a b := refl.tail hab +theorem le_reflTransGen : r ≤ ReflTransGen r := + fun _ _ ↦ single + theorem head (hab : r a b) (hbc : ReflTransGen r b c) : ReflTransGen r a c := by induction hbc with | refl => exact refl.tail hab @@ -497,13 +505,17 @@ theorem total_of_right_unique (U : Relator.RightUnique r) (ab : ReflTransGen r a end ReflTransGen -namespace TransGen - -theorem to_reflTransGen {a b} (h : TransGen r a b) : ReflTransGen r a b := by +theorem transGen_le_reflTransGen : TransGen r ≤ ReflTransGen r := by + intro a _ h induction h with | single h => exact ReflTransGen.single h | tail _ bc ab => exact ReflTransGen.tail ab bc +namespace TransGen + +theorem to_reflTransGen {a b} : TransGen r a b → ReflTransGen r a b := + transGen_le_reflTransGen a b + theorem trans_left (hab : TransGen r a b) (hbc : ReflTransGen r b c) : TransGen r a c := by induction hbc with | refl => assumption @@ -586,9 +598,8 @@ lemma reflGen_eq_self [Std.Refl r] : ReflGen r = r := by @[deprecated inferInstance (since := "2026-03-27")] lemma reflexive_reflGen : Std.Refl (ReflGen r) := inferInstance -lemma reflGen_minimal {r' : α → α → Prop} [Std.Refl r'] (h : ∀ x y, r x y → r' x y) {x y : α} - (hxy : ReflGen r x y) : r' x y := by - simpa [reflGen_eq_self] using ReflGen.mono h hxy +lemma reflGen_minimal {r' : α → α → Prop} [Std.Refl r'] (h : r ≤ r') : ReflGen r ≤ r' := by + simpa [reflGen_eq_self] using ReflGen.mono h end reflGen @@ -641,40 +652,39 @@ theorem transitive_transGen : IsTrans α (TransGen r) := inferInstance theorem transGen_idem : TransGen (TransGen r) = TransGen r := transGen_eq_self -theorem TransGen.lift {p : β → β → Prop} {a b : α} (f : α → β) (h : ∀ a b, r a b → p (f a) (f b)) - (hab : TransGen r a b) : TransGen p (f a) (f b) := by +theorem TransGen.lift {p : β → β → Prop} (f : α → β) (h : r ≤ (p on f)) : + TransGen r ≤ (TransGen p on f) := by + intro a _ hab induction hab with | single hac => exact TransGen.single (h a _ hac) | tail _ hcd hac => exact TransGen.tail hac (h _ _ hcd) -theorem TransGen.lift' {p : β → β → Prop} {a b : α} (f : α → β) - (h : ∀ a b, r a b → TransGen p (f a) (f b)) (hab : TransGen r a b) : - TransGen p (f a) (f b) := by +theorem TransGen.lift' {p : β → β → Prop} (f : α → β) (h : r ≤ (TransGen p on f)) : + TransGen r ≤ (TransGen p on f) := by + intro _ _ hab simpa [transGen_eq_self] using hab.lift f h -theorem TransGen.closed {p : α → α → Prop} : - (∀ a b, r a b → TransGen p a b) → TransGen r a b → TransGen p a b := +theorem TransGen.closed {p : α → α → Prop} : r ≤ TransGen p → TransGen r ≤ TransGen p := TransGen.lift' id lemma TransGen.closed' {P : α → Prop} (dc : ∀ {a b}, r a b → P b → P a) {a b : α} (h : TransGen r a b) : P b → P a := h.head_induction_on dc fun hr _ hi ↦ dc hr ∘ hi -theorem TransGen.mono {p : α → α → Prop} : - (∀ a b, r a b → p a b) → TransGen r a b → TransGen p a b := +theorem TransGen.mono {p : α → α → Prop} : r ≤ p → TransGen r ≤ TransGen p := TransGen.lift id -lemma transGen_minimal {r' : α → α → Prop} [IsTrans α r'] (h : ∀ x y, r x y → r' x y) {x y : α} - (hxy : TransGen r x y) : r' x y := by - simpa [transGen_eq_self] using TransGen.mono h hxy +lemma transGen_minimal {r' : α → α → Prop} [IsTrans α r'] (h : r ≤ r') : TransGen r ≤ r' := by + simpa [transGen_eq_self] using TransGen.mono h -theorem TransGen.swap (h : TransGen r b a) : TransGen (swap r) a b := by +theorem TransGen.swap : swap (TransGen r) ≤ TransGen (swap r) := by + intro _ _ h induction h with | single h => exact TransGen.single h | tail _ hbc ih => exact ih.head hbc theorem transGen_swap : TransGen (swap r) a b ↔ TransGen r b a := - ⟨TransGen.swap, TransGen.swap⟩ + ⟨TransGen.swap b a, TransGen.swap a b⟩ end TransGen @@ -696,12 +706,11 @@ theorem reflTransGen_iff_eq_or_transGen : ReflTransGen r a b ↔ b = a ∨ Trans · rfl · exact h.to_reflTransGen -theorem ReflTransGen.lift {p : β → β → Prop} {a b : α} (f : α → β) - (h : ∀ a b, r a b → p (f a) (f b)) (hab : ReflTransGen r a b) : ReflTransGen p (f a) (f b) := - ReflTransGen.trans_induction_on hab (fun _ ↦ refl) (ReflTransGen.single ∘ h _ _) fun _ _ ↦ trans +theorem ReflTransGen.lift {p : β → β → Prop} (f : α → β) (h : r ≤ (p on f)) : + ReflTransGen r ≤ (ReflTransGen p on f) := + fun _ _ hab ↦ trans_induction_on hab (fun _ ↦ refl) (single ∘ h _ _) fun _ _ ↦ trans -theorem ReflTransGen.mono {p : α → α → Prop} : (∀ a b, r a b → p a b) → - ReflTransGen r a b → ReflTransGen p a b := +theorem ReflTransGen.mono {p : α → α → Prop} : r ≤ p → ReflTransGen r ≤ ReflTransGen p := ReflTransGen.lift id @[grind =] @@ -734,22 +743,23 @@ theorem transitive_reflTransGen : IsTrans α (ReflTransGen r) := inferInstance theorem reflTransGen_idem : ReflTransGen (ReflTransGen r) = ReflTransGen r := reflTransGen_eq_self -theorem ReflTransGen.lift' {p : β → β → Prop} {a b : α} (f : α → β) - (h : ∀ a b, r a b → ReflTransGen p (f a) (f b)) - (hab : ReflTransGen r a b) : ReflTransGen p (f a) (f b) := by +theorem ReflTransGen.lift' {p : β → β → Prop} (f : α → β) (h : r ≤ (ReflTransGen p on f)) : + ReflTransGen r ≤ (ReflTransGen p on f) := by + intro _ _ hab simpa [reflTransGen_eq_self] using hab.lift f h theorem reflTransGen_closed {p : α → α → Prop} : - (∀ a b, r a b → ReflTransGen p a b) → ReflTransGen r a b → ReflTransGen p a b := + r ≤ ReflTransGen p → ReflTransGen r ≤ ReflTransGen p := ReflTransGen.lift' id -theorem ReflTransGen.swap (h : ReflTransGen r b a) : ReflTransGen (swap r) a b := by +theorem ReflTransGen.swap : swap (ReflTransGen r) ≤ ReflTransGen (swap r) := by + intro _ _ h induction h with | refl => rfl | tail _ hbc ih => exact ih.head hbc theorem reflTransGen_swap : ReflTransGen (swap r) a b ↔ ReflTransGen r b a := - ⟨ReflTransGen.swap, ReflTransGen.swap⟩ + ⟨ReflTransGen.swap b a, ReflTransGen.swap a b⟩ @[simp, grind =] lemma reflGen_transGen : ReflGen (TransGen r) = ReflTransGen r := by ext x y @@ -758,10 +768,10 @@ theorem reflTransGen_swap : ReflTransGen (swap r) a b ↔ ReflTransGen r b a := @[simp, grind =] lemma transGen_reflGen : TransGen (ReflGen r) = ReflTransGen r := by ext x y refine ⟨fun h ↦ ?_, fun h ↦ ?_⟩ - · simpa [reflTransGen_eq_self] using (h.mono fun _ _ ↦ ReflGen.to_reflTransGen).to_reflTransGen + · simpa [reflTransGen_eq_self] using h.mono reflGen_le_reflTransGen x y |>.to_reflTransGen · obtain (rfl | h) := reflTransGen_iff_eq_or_transGen.mp h · exact .single .refl - · exact TransGen.mono (fun _ _ ↦ .single) h + · exact h.mono (fun _ _ ↦ .single) x y @[simp, grind =] lemma reflTransGen_reflGen : ReflTransGen (ReflGen r) = ReflTransGen r := by simp only [← transGen_reflGen, reflGen_eq_self] @@ -794,8 +804,8 @@ see for example `Quot.eqvGen_exact` and `Quot.eqvGen_sound`. -/ def setoid : Setoid α := Setoid.mk _ (EqvGen.is_equivalence r) -theorem mono {r p : α → α → Prop} (hrp : ∀ a b, r a b → p a b) (h : EqvGen r a b) : - EqvGen p a b := by +theorem mono {r p : α → α → Prop} (hrp : r ≤ p) : EqvGen r ≤ EqvGen p := by + intro _ _ h induction h with | rel a b h => exact EqvGen.rel _ _ (hrp _ _ h) | refl => exact EqvGen.refl _ @@ -840,9 +850,10 @@ theorem church_rosser (h : ∀ a b c, r a b → r a c → ∃ d, ReflGen r b d | refl => exact ⟨b, hea, hcb⟩ | single hba => exact ⟨a, hea, hcb.tail hba⟩ +theorem le_join_of_refl [Std.Refl r] : r ≤ Join r := + fun _ b hab ↦ ⟨b, hab, refl b⟩ -theorem join_of_single [Std.Refl r] (hab : r a b) : Join r a b := - ⟨b, hab, refl b⟩ +@[deprecated (since := "2026-06-30")] alias join_of_single := le_join_of_refl protected instance Join.symm : Std.Symm (Join r) where symm _ _ := fun ⟨c, hac, hcb⟩ ↦ ⟨c, hcb, hac⟩ @@ -871,23 +882,30 @@ theorem equivalence_join_reflTransGen Equivalence (Join (ReflTransGen r)) := equivalence_join fun _ _ _ ↦ church_rosser h -theorem join_of_equivalence {r' : α → α → Prop} (hr : Equivalence r) (h : ∀ a b, r' a b → r a b) : - Join r' a b → r a b - | ⟨_, hac, hbc⟩ => hr.trans (h _ _ hac) (hr.symm <| h _ _ hbc) +theorem join_le_of_equivalence_of_le {r' : α → α → Prop} (hr : Equivalence r) (h : r' ≤ r) : + Join r' ≤ r := + fun a b ⟨c, hac, hbc⟩ ↦ hr.trans (h a c hac) (hr.symm <| h b c hbc) -theorem reflTransGen_of_isTrans_reflexive {r' : α → α → Prop} [Std.Refl r] [IsTrans α r] - (h : ∀ a b, r' a b → r a b) (h' : ReflTransGen r' a b) : r a b := by - simpa [reflTransGen_eq_self] using ReflTransGen.mono h h' +@[deprecated (since := "2026-06-30")] alias join_of_equivalence := join_le_of_equivalence_of_le + +theorem reflTransGen_le_of_le {r' : α → α → Prop} [Std.Refl r] [IsTrans α r] + (h : r' ≤ r) : ReflTransGen r' ≤ r := by + simpa [reflTransGen_eq_self] using ReflTransGen.mono h + +@[deprecated (since := "2026-06-30")] +alias reflTransGen_of_isTrans_reflexive := reflTransGen_le_of_le @[deprecated (since := "2026-02-21")] -alias reflTransGen_of_transitive_reflexive := reflTransGen_of_isTrans_reflexive +alias reflTransGen_of_transitive_reflexive := reflTransGen_le_of_le + +@[deprecated (since := "2025-12-17")] alias reflTransGen_minimal := reflTransGen_le_of_le -@[deprecated (since := "2025-12-17")] alias reflTransGen_minimal := - reflTransGen_of_transitive_reflexive +theorem reflTransGen_le_of_equivalence_of_le {r' : α → α → Prop} (hr : Equivalence r) : + r' ≤ r → ReflTransGen r' ≤ r := + @reflTransGen_le_of_le _ _ _ hr.stdRefl hr.isTrans -theorem reflTransGen_of_equivalence {r' : α → α → Prop} (hr : Equivalence r) : - (∀ a b, r' a b → r a b) → ReflTransGen r' a b → r a b := - @reflTransGen_of_isTrans_reflexive _ _ _ _ _ hr.stdRefl hr.isTrans +@[deprecated (since := "2026-06-30")] +alias reflTransGen_of_equivalence := reflTransGen_le_of_equivalence_of_le end Join diff --git a/Mathlib/Logic/Small/Basic.lean b/Mathlib/Logic/Small/Basic.lean index eb7769fbd3a1cc..2f947ce5c2af91 100644 --- a/Mathlib/Logic/Small/Basic.lean +++ b/Mathlib/Logic/Small/Basic.lean @@ -75,3 +75,5 @@ instance small_quot {α : Type u} [Small.{v} α] (r : α → α → Prop) : Smal instance small_quotient {α : Type u} [Small.{v} α] (s : Setoid α) : Small.{v} (Quotient s) := small_of_surjective Quotient.mk_surjective + +instance small_orderDual {α : Type*} [h : Small.{v} α] : Small.{v} αᵒᵈ := h diff --git a/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean b/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean index 9bf51c1660b896..966522788b9a5e 100644 --- a/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean +++ b/Mathlib/MeasureTheory/Constructions/BorelSpace/Basic.lean @@ -7,8 +7,6 @@ module public import Mathlib.MeasureTheory.Group.Arithmetic public import Mathlib.Topology.GDelta.MetrizableSpace -public import Mathlib.Topology.Instances.EReal.Lemmas -public import Mathlib.Topology.Instances.Rat /-! # Borel (measurable) space @@ -500,7 +498,8 @@ is ae-measurable. -/ theorem Continuous.aemeasurable {f : α → γ} (h : Continuous f) {μ : Measure α} : AEMeasurable f μ := h.measurable.aemeasurable -theorem IsClosedEmbedding.measurable {f : α → γ} (hf : IsClosedEmbedding f) : Measurable f := +theorem Topology.IsClosedEmbedding.measurable {f : α → γ} (hf : IsClosedEmbedding f) : + Measurable f := hf.continuous.measurable /-- If a function is defined piecewise in terms of functions which are continuous on their diff --git a/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean b/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean index b96f72d46a923a..69fa66fdf6c2eb 100644 --- a/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean +++ b/Mathlib/MeasureTheory/Constructions/BorelSpace/Metrizable.lean @@ -103,7 +103,7 @@ theorem measurable_of_tendsto_metrizable_ae {μ : Measure α} [μ.IsComplete] {f aemeasurable_iff_measurable.mp (aemeasurable_of_tendsto_metrizable_ae' (fun i => (hf i).aemeasurable) h_ae_tendsto) -theorem measurable_limit_of_tendsto_metrizable_ae {ι} [Countable ι] [Nonempty ι] {μ : Measure α} +theorem measurable_limit_of_tendsto_metrizable_ae {ι} [Nonempty ι] {μ : Measure α} {f : ι → α → β} {L : Filter ι} [L.IsCountablyGenerated] (hf : ∀ n, AEMeasurable (f n) μ) (h_ae_tendsto : ∀ᵐ x ∂μ, ∃ l : β, Tendsto (fun n => f n x) L (𝓝 l)) : ∃ f_lim : α → β, Measurable f_lim ∧ ∀ᵐ x ∂μ, Tendsto (fun n => f n x) L (𝓝 (f_lim x)) := by diff --git a/Mathlib/MeasureTheory/Constructions/BorelSpace/Order.lean b/Mathlib/MeasureTheory/Constructions/BorelSpace/Order.lean index e21b260f6f9a0e..20c22dc9b98969 100644 --- a/Mathlib/MeasureTheory/Constructions/BorelSpace/Order.lean +++ b/Mathlib/MeasureTheory/Constructions/BorelSpace/Order.lean @@ -5,7 +5,6 @@ Authors: Johannes Hölzl, Yury Kudryashov, Kexing Ying -/ module -public import Mathlib.Topology.Semicontinuity.Basic public import Mathlib.MeasureTheory.Function.AEMeasurableSequence public import Mathlib.MeasureTheory.Order.Lattice public import Mathlib.Topology.Order.Lattice diff --git a/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean b/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean index c47e8ea5d91b12..ef30b4946bc384 100644 --- a/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean +++ b/Mathlib/MeasureTheory/Constructions/BorelSpace/Real.lean @@ -7,7 +7,7 @@ module public import Mathlib.MeasureTheory.Constructions.BorelSpace.Order public import Mathlib.MeasureTheory.MeasurableSpace.Prod -public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms +public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass public import Mathlib.Topology.Instances.Real.Lemmas /-! @@ -374,7 +374,7 @@ theorem Measurable.nnreal_tsum {ι} [Countable ι] {f : ι → α → ℝ≥0} ( @[fun_prop, deprecated "Use `AEMeasurable.tsum` from `Mathlib.MeasureTheory.Constructions.Polish.Basic` instead" - (since := "2026-04-30")] + (since := "2026-04-30")] theorem AEMeasurable.ennreal_tsum {ι} [Countable ι] {f : ι → α → ℝ≥0∞} {μ : Measure α} (h : ∀ i, AEMeasurable (f i) μ) : AEMeasurable (fun x => ∑' i, f i x) μ := by simp_rw [ENNReal.tsum_eq_iSup_sum] @@ -573,7 +573,7 @@ lemma tendsto_measure_Icc_nhdsWithin_right (b : ℝ) : intro s hs simpa using mem_of_mem_nhds hs -lemma tendsto_measure_Icc [NoAtoms μ] (b : ℝ) : +lemma tendsto_measure_Icc [NullSingletonClass μ] (b : ℝ) : Tendsto (fun δ ↦ μ (Icc (b - δ) (b + δ))) (𝓝 (0 : ℝ)) (𝓝 0) := by rw [← nhdsLT_sup_nhdsGE, tendsto_sup] constructor diff --git a/Mathlib/MeasureTheory/Constructions/HaarToSphere.lean b/Mathlib/MeasureTheory/Constructions/HaarToSphere.lean index 73adacc13b0eb5..63ebf1c46e112e 100644 --- a/Mathlib/MeasureTheory/Constructions/HaarToSphere.lean +++ b/Mathlib/MeasureTheory/Constructions/HaarToSphere.lean @@ -9,7 +9,6 @@ public import Mathlib.Algebra.Order.Field.Pointwise public import Mathlib.Analysis.Normed.Module.Ball.RadialEquiv public import Mathlib.Analysis.SpecialFunctions.Integrals.Basic public import Mathlib.MeasureTheory.Integral.Prod -public import Mathlib.MeasureTheory.Measure.Lebesgue.EqHaar /-! # Generalized polar coordinate change diff --git a/Mathlib/MeasureTheory/Constructions/Pi.lean b/Mathlib/MeasureTheory/Constructions/Pi.lean index eb13f8536072d9..c025ed63bbb813 100644 --- a/Mathlib/MeasureTheory/Constructions/Pi.lean +++ b/Mathlib/MeasureTheory/Constructions/Pi.lean @@ -9,8 +9,6 @@ public import Mathlib.Algebra.BigOperators.Fin public import Mathlib.Logic.Encodable.Pi public import Mathlib.MeasureTheory.Group.Measure public import Mathlib.MeasureTheory.MeasurableSpace.Pi -public import Mathlib.MeasureTheory.Measure.Prod -public import Mathlib.Topology.Constructions /-! # Indexed product measures @@ -410,11 +408,12 @@ lemma _root_.MeasureTheory.measurePreserving_eval [∀ i, IsProbabilityMeasure ( rw [Measure.pi_map_eval, Finset.prod_eq_one, one_smul] exact fun _ _ ↦ measure_univ -theorem pi_hyperplane (i : ι) [NoAtoms (μ i)] (x : α i) : +theorem pi_hyperplane (i : ι) [NullSingletonClass (μ i)] (x : α i) : Measure.pi μ { f : ∀ i, α i | f i = x } = 0 := show Measure.pi μ (eval i ⁻¹' {x}) = 0 from pi_eval_preimage_null _ (measure_singleton x) -theorem ae_eval_ne (i : ι) [NoAtoms (μ i)] (x : α i) : ∀ᵐ y : ∀ i, α i ∂Measure.pi μ, y i ≠ x := +theorem ae_eval_ne (i : ι) [NullSingletonClass (μ i)] (x : α i) : + ∀ᵐ y : ∀ i, α i ∂Measure.pi μ, y i ≠ x := compl_mem_ae_iff.2 (pi_hyperplane μ i x) theorem restrict_pi_pi (s : (i : ι) → Set (α i)) : @@ -467,7 +466,7 @@ lemma pi_map_piOptionEquivProd {β : Option ι → Type*} [∀ i, MeasurableSpac section Intervals -variable [∀ i, PartialOrder (α i)] [∀ i, NoAtoms (μ i)] +variable [∀ i, PartialOrder (α i)] [∀ i, NullSingletonClass (μ i)] theorem pi_Iio_ae_eq_pi_Iic {s : Set ι} {f : ∀ i, α i} : (pi s fun i => Iio (f i)) =ᵐ[Measure.pi μ] pi s fun i => Iic (f i) := @@ -515,18 +514,27 @@ theorem univ_pi_Ico_ae_eq_Icc {f g : ∀ i, α i} : end Intervals -/-- If one of the measures `μ i` has no atoms, them `Measure.pi µ` -has no atoms. The instance below assumes that all `μ i` have no atoms. -/ -theorem pi_noAtoms (i : ι) [NoAtoms (μ i)] : NoAtoms (Measure.pi μ) := +/-- If one of the measures `μ i` has value zero on singeltons, them `Measure.pi µ` +has value zero on singletons. The instance below assumes that all `μ i` have value zero on +singletons. -/ +theorem pi_nullSingletonClass (i : ι) [NullSingletonClass (μ i)] : + NullSingletonClass (Measure.pi μ) := ⟨fun x => flip measure_mono_null (pi_hyperplane μ i (x i)) (singleton_subset_iff.2 rfl)⟩ -instance pi_noAtoms' [h : Nonempty ι] [∀ i, NoAtoms (μ i)] : NoAtoms (Measure.pi μ) := - h.elim fun i => pi_noAtoms i +@[deprecated (since := "2026-06-09")] +alias pi_noAtoms := pi_nullSingletonClass + +instance pi_nullSingletonClass' [h : Nonempty ι] [∀ i, NullSingletonClass (μ i)] : + NullSingletonClass (Measure.pi μ) := + h.elim fun i => pi_nullSingletonClass i + +@[deprecated (since := "2026-06-09")] +alias pi_noAtoms' := pi_nullSingletonClass' instance {α : ι → Type*} [Nonempty ι] [∀ i, MeasureSpace (α i)] - [∀ i, SigmaFinite (volume : Measure (α i))] [∀ i, NoAtoms (volume : Measure (α i))] : - NoAtoms (volume : Measure (∀ i, α i)) := - pi_noAtoms' + [∀ i, SigmaFinite (volume : Measure (α i))] [∀ i, NullSingletonClass (volume : Measure (α i))] : + NullSingletonClass (volume : Measure (∀ i, α i)) := + pi_nullSingletonClass' instance pi.isLocallyFiniteMeasure [∀ i, TopologicalSpace (α i)] [∀ i, IsLocallyFiniteMeasure (μ i)] : diff --git a/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean b/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean index ea39c5a407cc7f..c1c595f7b275db 100644 --- a/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean +++ b/Mathlib/MeasureTheory/Constructions/Polish/Basic.lean @@ -6,6 +6,7 @@ Authors: Sébastien Gouëzel, Felix Weilacher module public import Mathlib.MeasureTheory.Constructions.BorelSpace.Metrizable +public import Mathlib.Tactic.CrossRefAttribute public import Mathlib.Topology.MetricSpace.Perfect public import Mathlib.Topology.Separation.CountableSeparatingOn @@ -76,6 +77,7 @@ the natural topology in a space is non-Polish. To endow a standard Borel space `α` with a compatible Polish topology, use `letI := upgradeStandardBorel α`. One can then use `eq_borel_upgradeStandardBorel α` to rewrite the `MeasurableSpace α` instance to `borel α t`, where `t` is the new topology. -/ +@[wikidata Q25378068] class StandardBorelSpace [MeasurableSpace α] : Prop where /-- There exists a compatible Polish topology. -/ polish : ∃ _ : TopologicalSpace α, BorelSpace α ∧ PolishSpace α diff --git a/Mathlib/MeasureTheory/Constructions/Polish/StronglyMeasurable.lean b/Mathlib/MeasureTheory/Constructions/Polish/StronglyMeasurable.lean index 1676161053d094..2582708a2b5916 100644 --- a/Mathlib/MeasureTheory/Constructions/Polish/StronglyMeasurable.lean +++ b/Mathlib/MeasureTheory/Constructions/Polish/StronglyMeasurable.lean @@ -82,12 +82,22 @@ variable {X E ι : Type*} [MeasurableSpace X] [CommMonoid E] [TopologicalSpace E section -variable [IsCompletelyPseudoMetrizableSpace E] [ContinuousMul E] - [Countable ι] {L : SummationFilter ι} [L.NeBot] [L.filter.IsCountablyGenerated] +variable [ContinuousMul E] {L : SummationFilter ι} [L.NeBot] [L.filter.IsCountablyGenerated] -/-- The product of strongly measurable functions is measurable. -/ +/-- The infinite product of strongly measurable functions is measurable, `HasProd` version. -/ @[to_additive (attr := fun_prop) -/-- The sum of strongly measurable functions is measurable. -/] +/-- The infinite sum of strongly measurable functions is measurable, `HasSum` version. -/] +theorem StronglyMeasurable.hasProd [PseudoMetrizableSpace E] {f : ι → X → E} {g : X → E} + (h : ∀ i : ι, StronglyMeasurable (f i)) (h' : ∀ x, HasProd (fun i ↦ f i x) (g x) L) : + StronglyMeasurable g := by + refine stronglyMeasurable_of_tendsto L.filter ?_ (tendsto_pi_nhds.mpr h') + fun_prop + +variable [IsCompletelyPseudoMetrizableSpace E] [Countable ι] + +/-- The infinite product of strongly measurable functions is measurable. -/ +@[to_additive (attr := fun_prop) +/-- The infinite sum of strongly measurable functions is measurable. -/] theorem StronglyMeasurable.tprod {f : ι → X → E} (h : ∀ i : ι, StronglyMeasurable (f i)) : StronglyMeasurable (fun x => ∏'[L] i : ι, f i x) := by let E := { x | Multipliable (f · x) L } diff --git a/Mathlib/MeasureTheory/Constructions/UnitInterval.lean b/Mathlib/MeasureTheory/Constructions/UnitInterval.lean index b84fdf2a05dbae..0b39ca13edfa63 100644 --- a/Mathlib/MeasureTheory/Constructions/UnitInterval.lean +++ b/Mathlib/MeasureTheory/Constructions/UnitInterval.lean @@ -11,7 +11,7 @@ public import Mathlib.MeasureTheory.Measure.Haar.Unique # The canonical measure on the unit interval This file provides a `MeasureTheory.MeasureSpace` instance on `unitInterval`, -and shows it is a probability measure with no atoms. +and shows it is a probability measure with value zero on singletons. It also contains some basic results on the volume of various interval sets. -/ @@ -43,7 +43,7 @@ lemma volume_apply {s : Set I} : volume s = volume (Subtype.val '' s) := lemma measurePreserving_coe : MeasurePreserving ((↑) : I → ℝ) volume (volume.restrict I) := measurePreserving_subtype_coe measurableSet_Icc -instance : NoAtoms (volume : Measure I) where +instance : NullSingletonClass (volume : Measure I) where measure_singleton x := by simp [volume_apply] @[fun_prop] diff --git a/Mathlib/MeasureTheory/Covering/Besicovitch.lean b/Mathlib/MeasureTheory/Covering/Besicovitch.lean index c369663b90f5d7..efe2280ce46e21 100644 --- a/Mathlib/MeasureTheory/Covering/Besicovitch.lean +++ b/Mathlib/MeasureTheory/Covering/Besicovitch.lean @@ -138,10 +138,10 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: `Besicovitch.SatelliteConfig.r`. -/ @[positivity Besicovitch.SatelliteConfig.r _ _] -meta def evalBesicovitchSatelliteConfigR : PositivityExt where eval {u α} _zα pα? e := do +meta def evalBesicovitchSatelliteConfigR : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@Besicovitch.SatelliteConfig.r $β $inst $N $τ $self $i) => - let some _ := pα? | pure .none assertInstancesCommute return .positive q(Besicovitch.SatelliteConfig.rpos $self $i) | _, _, _ => throwError "not Besicovitch.SatelliteConfig.r" @@ -590,11 +590,7 @@ theorem exist_finset_disjoint_balls_large_measure (μ : Measure α) [IsFiniteMea apply ENNReal.exists_le_of_sum_le _ S exact ⟨⟨0, bot_lt_iff_ne_bot.2 Npos⟩, Finset.mem_univ _⟩ replace hi : μ s / (N + 1) < μ (s ∩ v i) := by - apply lt_of_lt_of_le _ hi - apply (ENNReal.mul_lt_mul_iff_right hμs.ne' (by finiteness)).2 - rw [ENNReal.inv_lt_inv] - conv_lhs => rw [← add_zero (N : ℝ≥0∞)] - exact ENNReal.add_lt_add_left (by finiteness) zero_lt_one + grw [← hi, ← _root_.zero_lt_one, add_zero] <;> finiteness have B : μ (o ∩ v i) = ∑' x : u i, μ (o ∩ closedBall x (r x)) := by have : o ∩ v i = ⋃ (x : s) (_ : x ∈ u i), o ∩ closedBall x (r x) := by simp only [v, inter_iUnion] diff --git a/Mathlib/MeasureTheory/Covering/BesicovitchVectorSpace.lean b/Mathlib/MeasureTheory/Covering/BesicovitchVectorSpace.lean index 932bfe42ec0516..2d53c3ab68fcff 100644 --- a/Mathlib/MeasureTheory/Covering/BesicovitchVectorSpace.lean +++ b/Mathlib/MeasureTheory/Covering/BesicovitchVectorSpace.lean @@ -8,7 +8,6 @@ module public import Mathlib.MeasureTheory.Measure.Lebesgue.EqHaar public import Mathlib.MeasureTheory.Covering.Besicovitch public import Mathlib.Tactic.AdaptationNote -public import Mathlib.Algebra.EuclideanDomain.Basic /-! # Satellite configurations for Besicovitch covering lemma in vector spaces diff --git a/Mathlib/MeasureTheory/Covering/DensityTheorem.lean b/Mathlib/MeasureTheory/Covering/DensityTheorem.lean index 27704ab5d8d7e9..f1e975f25ca496 100644 --- a/Mathlib/MeasureTheory/Covering/DensityTheorem.lean +++ b/Mathlib/MeasureTheory/Covering/DensityTheorem.lean @@ -5,7 +5,6 @@ Authors: Oliver Nash -/ module -public import Mathlib.MeasureTheory.Measure.Doubling public import Mathlib.MeasureTheory.Covering.Vitali public import Mathlib.MeasureTheory.Covering.Differentiation diff --git a/Mathlib/MeasureTheory/Covering/Differentiation.lean b/Mathlib/MeasureTheory/Covering/Differentiation.lean index e90d23eca85b0d..7daede20a4718e 100644 --- a/Mathlib/MeasureTheory/Covering/Differentiation.lean +++ b/Mathlib/MeasureTheory/Covering/Differentiation.lean @@ -9,7 +9,6 @@ public import Mathlib.MeasureTheory.Covering.VitaliFamily public import Mathlib.MeasureTheory.Function.AEMeasurableOrder public import Mathlib.MeasureTheory.Integral.Average public import Mathlib.MeasureTheory.Measure.Decomposition.Lebesgue -public import Mathlib.MeasureTheory.Measure.Regular /-! # Differentiation of measures @@ -201,9 +200,7 @@ theorem ae_eventually_measure_zero_of_singular (hρ : ρ ⟂ₘ μ) : obtain ⟨n, hn⟩ : ∃ n, u n < w := ((tendsto_order.1 u_lim).2 w (ENNReal.coe_pos.1 w_pos)).exists filter_upwards [hx n, h'x, v.eventually_measure_lt_top x] intro a ha μa_pos μa_lt_top - grw [ENNReal.div_lt_iff (.inl μa_pos.ne') (.inl μa_lt_top.ne), ha, hn] - gcongr - exact μa_lt_top.ne + grw [ENNReal.div_lt_iff (.inl μa_pos.ne') (.inl μa_lt_top.ne), ha, hn, w_lt] section AbsolutelyContinuous diff --git a/Mathlib/MeasureTheory/Covering/Vitali.lean b/Mathlib/MeasureTheory/Covering/Vitali.lean index bc367b164c4593..f708ea56c5007e 100644 --- a/Mathlib/MeasureTheory/Covering/Vitali.lean +++ b/Mathlib/MeasureTheory/Covering/Vitali.lean @@ -7,7 +7,6 @@ module public import Mathlib.MeasureTheory.Constructions.BorelSpace.Basic public import Mathlib.MeasureTheory.Covering.VitaliFamily -public import Mathlib.Data.Set.Pairwise.Lattice /-! # Vitali covering theorems diff --git a/Mathlib/MeasureTheory/Function/AEEqFun/DomAct.lean b/Mathlib/MeasureTheory/Function/AEEqFun/DomAct.lean index 3e35fb46c6846f..06992682a92cf0 100644 --- a/Mathlib/MeasureTheory/Function/AEEqFun/DomAct.lean +++ b/Mathlib/MeasureTheory/Function/AEEqFun/DomAct.lean @@ -6,8 +6,6 @@ Authors: Yury Kudryashov module public import Mathlib.MeasureTheory.Function.AEEqFun -public import Mathlib.MeasureTheory.Group.Action -public import Mathlib.GroupTheory.GroupAction.DomAct.Basic public import Mathlib.MeasureTheory.Function.StronglyMeasurable.Lemmas /-! # Action of `DomMulAct` and `DomAddAct` on `α →ₘ[μ] β` diff --git a/Mathlib/MeasureTheory/Function/AEMeasurableSequence.lean b/Mathlib/MeasureTheory/Function/AEMeasurableSequence.lean index 93dc03e76b71ef..498d2a02cd2f96 100644 --- a/Mathlib/MeasureTheory/Function/AEMeasurableSequence.lean +++ b/Mathlib/MeasureTheory/Function/AEMeasurableSequence.lean @@ -5,7 +5,6 @@ Authors: Rémy Degenne -/ module -public import Mathlib.MeasureTheory.MeasurableSpace.Basic public import Mathlib.MeasureTheory.Measure.MeasureSpaceDef /-! diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean index fda5b38bd6fb45..049a6f52ee004e 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/Basic.lean @@ -6,6 +6,7 @@ Authors: Rémy Degenne module public import Mathlib.MeasureTheory.Function.ConditionalExpectation.CondexpL1 +public import Mathlib.Tactic.CrossRefAttribute import Mathlib.MeasureTheory.Function.LpSpace.InfiniteSum @@ -97,6 +98,7 @@ It is defined as 0 if any one of the following conditions is true: - `m` is not a sub-σ-algebra of `m₀`, - `μ` is not σ-finite with respect to `m`, - `f` is not integrable. -/ +@[wikidata Q772232] noncomputable irreducible_def condExp (μ : Measure[m₀] α) (f : α → E) : α → E := if hm : m ≤ m₀ then if h : SigmaFinite (μ.trim hm) ∧ Integrable f μ then @@ -402,33 +404,6 @@ end RCLike end NormedSpace -section Real -variable [InnerProductSpace ℝ E] [CompleteSpace E] - --- TODO: Generalize via the conditional Jensen inequality -lemma eLpNorm_condExp_le : eLpNorm (μ[f | m]) 2 μ ≤ eLpNorm f 2 μ := by - by_cases hm : m ≤ m₀; swap - · simp [condExp_of_not_le hm] - by_cases hfμ : SigmaFinite (μ.trim hm); swap - · rw [condExp_of_not_sigmaFinite hm hfμ] - simp - by_cases hfi : Integrable f μ; swap - · rw [condExp_of_not_integrable hfi] - simp - obtain hf | hf := eq_or_ne (eLpNorm f 2 μ) ∞ - · simp [hf] - replace hf : MemLp f 2 μ := ⟨hfi.1, Ne.lt_top' fun a ↦ hf a.symm⟩ - rw [← eLpNorm_congr_ae (hf.condExpL2_ae_eq_condExp' (𝕜 := ℝ) hm hfi)] - refine le_trans (eLpNorm_condExpL2_le hm _) ?_ - rw [eLpNorm_congr_ae hf.coeFn_toLp] - -protected lemma MemLp.condExp (hf : MemLp f 2 μ) : MemLp (μ[f | m]) 2 μ := by - by_cases hm : m ≤ m₀ - · exact ⟨(stronglyMeasurable_condExp.mono hm).aestronglyMeasurable, - eLpNorm_condExp_le.trans_lt hf.eLpNorm_lt_top⟩ - · simp [condExp_of_not_le hm] - -end Real end NormedAddCommGroup section NormedRing diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean index 7d25c166b20b66..bea52d41254491 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondJensen.lean @@ -243,7 +243,7 @@ theorem ConcaveOn.condExp_map_le_trim_univ {mE : MeasurableSpace E} [BorelSpace /-- In a Banach space `E` with a measure `μ`, then for any `f : α → E`, we have `‖𝔼[f | m]‖ ≤ᵐ[μ] 𝔼[‖f‖ | m]`. -/ -theorem norm_condExp_le : (‖μ[f | m] ·‖) ≤ᵐ[μ] μ[(‖f ·‖) | m] := by +theorem norm_condExp_le (f : α → E) : (‖μ[f | m] ·‖) ≤ᵐ[μ] μ[(‖f ·‖) | m] := by by_cases! hm : ¬ m ≤ mα · simp [condExp_of_not_le hm]; aesop by_cases! hμm : ¬ SigmaFinite (μ.trim hm) @@ -255,6 +255,24 @@ theorem norm_condExp_le : (‖μ[f | m] ·‖) ≤ᵐ[μ] μ[(‖f ·‖) | m] : exact convexOn_univ_norm.map_condExp_le_univ hm continuous_norm.lowerSemicontinuous hf_int hf_int.norm +theorem Integrable.norm_condExp_rpow_le {p : ℝ} (hp : 1 ≤ p) + (hfint : Integrable (fun x => ‖f x‖ ^ p) μ) : + (‖μ[f | m] ·‖ ^ p) ≤ᵐ[μ] μ[(‖f ·‖ ^ p) | m] := by + have hp' : 0 < p := by linarith + by_cases! hm : ¬ m ≤ mα + · simp [condExp_of_not_le hm, Real.zero_rpow hp'.ne.symm]; aesop + by_cases! hμm : ¬ SigmaFinite (μ.trim hm) + · simp [condExp_of_not_sigmaFinite hm hμm, Real.zero_rpow hp'.ne.symm]; aesop + by_cases! hf_int : ¬ Integrable f μ + · simp only [condExp_of_not_integrable hf_int, Pi.zero_apply, norm_zero, + Real.zero_rpow hp'.ne.symm] + apply condExp_nonneg + filter_upwards with a; positivity + have hl := (Real.continuous_rpow_const hp'.le).lowerSemicontinuous.lowerSemicontinuousOn (Ici 0) + have := (convexOn_rpow hp).map_condExp_le hm hl (by simp) isClosed_Ici hf_int.norm hfint + filter_upwards [norm_condExp_le f, this] with a ha hb + exact (Real.rpow_le_rpow (norm_nonneg _) ha hp'.le).trans hb + /-- **Conditional Jensen's inequality**: in a finite dimensional Banach space `E` with a measure `μ` that is σ-finite on a sub-σ-algebra `m`, if `φ : E → ℝ` is convex, then for any `f : α → E` such that `f` and `φ ∘ f` are integrable, we have `φ (𝔼[f | m]) ≤ᵐ[μ] 𝔼[φ ∘ f | m]`. -/ diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean index 02e96a6e6284dd..2e679c127c22f6 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL1.lean @@ -7,7 +7,6 @@ module public import Mathlib.MeasureTheory.Function.LpSpace.CompleteOfCompleteLp public import Mathlib.MeasureTheory.Function.ConditionalExpectation.CondexpL2 -public import Mathlib.MeasureTheory.Measure.Real /-! # Conditional expectation in L1 diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL2.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL2.lean index 34ade67b737921..11d4636a70d5e6 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL2.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/CondexpL2.lean @@ -5,7 +5,6 @@ Authors: Rémy Degenne -/ module -public import Mathlib.Analysis.InnerProductSpace.Projection.Basic public import Mathlib.MeasureTheory.Function.ConditionalExpectation.Unique public import Mathlib.MeasureTheory.Function.L2Space diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/LebesgueBochner.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/LebesgueBochner.lean index 9f349a7be4eaba..8da069eb28fccc 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/LebesgueBochner.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/LebesgueBochner.lean @@ -6,8 +6,8 @@ Authors: Rémy Degenne module -public import Mathlib.MeasureTheory.Function.ConditionalExpectation.Basic public import Mathlib.MeasureTheory.Function.ConditionalLExpectation +public import Mathlib.MeasureTheory.Function.ConditionalExpectation.Basic /-! # Results about both conditional expectations diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/RadonNikodym.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/RadonNikodym.lean index 14f9101d8a0118..a4cfeffc82916c 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/RadonNikodym.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/RadonNikodym.lean @@ -5,9 +5,8 @@ Authors: Rémy Degenne -/ module -public import Mathlib.MeasureTheory.Function.ConditionalExpectation.Basic public import Mathlib.MeasureTheory.Function.ConditionalLExpectation -public import Mathlib.MeasureTheory.Measure.Decomposition.Lebesgue +public import Mathlib.MeasureTheory.Function.ConditionalExpectation.Basic import Mathlib.MeasureTheory.Measure.Decomposition.IntegralRNDeriv import Mathlib.MeasureTheory.Function.ConditionalExpectation.LebesgueBochner diff --git a/Mathlib/MeasureTheory/Function/ConditionalExpectation/Real.lean b/Mathlib/MeasureTheory/Function/ConditionalExpectation/Real.lean index 8ff2777d878b7e..24c3958e3457f9 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalExpectation/Real.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalExpectation/Real.lean @@ -9,6 +9,9 @@ public import Mathlib.MeasureTheory.Function.ConditionalExpectation.Indicator public import Mathlib.MeasureTheory.Function.UniformIntegrable public import Mathlib.MeasureTheory.VectorMeasure.Decomposition.RadonNikodym +import Mathlib.MeasureTheory.Function.ConditionalExpectation.CondJensen +import Mathlib.MeasureTheory.Function.LpSeminorm.LpNorm + /-! # Conditional expectation of real-valued functions @@ -21,8 +24,6 @@ This file proves some results regarding the conditional expectation of real-valu Radon-Nikodym derivative of `fμ` restricted on `m` with respect to `μ` restricted on `m`. * `MeasureTheory.Integrable.uniformIntegrable_condExp`: the conditional expectation of a function form a uniformly integrable class. -* `MeasureTheory.condExp_mul_of_stronglyMeasurable_left`: the pull-out property of the conditional - expectation. -/ @@ -55,88 +56,105 @@ theorem rnDeriv_ae_eq_condExp {hm : m ≤ m0} [hμm : SigmaFinite (μ.trim hm)] exact (SignedMeasure.measurable_rnDeriv _ _).stronglyMeasurable · exact (SignedMeasure.measurable_rnDeriv _ _).stronglyMeasurable.aestronglyMeasurable --- TODO: the following couple of lemmas should be generalized and proved using Jensen's inequality --- for the conditional expectation (not in mathlib yet) . -theorem eLpNorm_one_condExp_le_eLpNorm (f : α → ℝ) : eLpNorm (μ[f | m]) 1 μ ≤ eLpNorm f 1 μ := by - by_cases hf : Integrable f μ - swap; · rw [condExp_of_not_integrable hf, eLpNorm_zero]; exact zero_le - by_cases hm : m ≤ m0 - swap; · rw [condExp_of_not_le hm, eLpNorm_zero]; exact zero_le - by_cases hsig : SigmaFinite (μ.trim hm) - swap; · rw [condExp_of_not_sigmaFinite hm hsig, eLpNorm_zero]; exact zero_le +section HasSolidNorm + +variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E] + +lemma condExp_le_nonneg_const [PartialOrder E] [ClosedIciTopology E] [IsOrderedAddMonoid E] + [IsOrderedModule ℝ E] {f : α → E} {c : E} (hc : 0 ≤ c) (hfc : ∀ᵐ x ∂μ, f x ≤ c) : + ∀ᵐ x ∂μ, μ[f | m] x ≤ c := by + by_cases! hm : ¬ m ≤ m0 + · filter_upwards with a using by simpa [condExp_of_not_le hm] + by_cases! hfint : ¬ Integrable f μ + · filter_upwards with a using by simpa [condExp_of_not_integrable hfint] + by_cases! hsig : ¬ SigmaFinite (μ.trim hm) + · filter_upwards with a using by simpa [condExp_of_not_sigmaFinite hm hsig] + refine (isCountablySpanning_spanningSets (μ.trim hm)).null_of_forall_restrict_null ?_ ?_ <;> + rintro - ⟨n, rfl⟩ + · exact hm _ (measurableSet_spanningSets (μ.trim hm) n) + · have h1 := condExp_restrict_ae_eq_restrict hm (measurableSet_spanningSets (μ.trim hm) n) hfint + have h2 := condExp_mono (μ := μ.restrict (spanningSets (μ.trim hm) n)) (m := m) + hfint.restrict (integrable_const c) (ae_restrict_of_ae hfc) + filter_upwards [h1, h2] with a ha hb + grw [← ha, hb, condExp_const hm] + +variable [Lattice E] [HasSolidNorm E] [IsOrderedAddMonoid E] [IsOrderedModule ℝ E] + +theorem abs_condExp_ae_le_condExp_abs (f : α → E) : |(μ[f | m])| ≤ᵐ[μ] μ[|f| | m] := by + by_cases! hfint : ¬Integrable f μ + · simp only [condExp_of_not_integrable hfint, abs_zero] + apply condExp_nonneg + filter_upwards with a using abs_nonneg (f a) + have h1 := condExp_mono (m := m) hfint hfint.abs (.of_forall (fun x => le_abs_self f x)) + have h2 := condExp_mono (m := m) hfint.neg hfint.abs (.of_forall fun x => neg_le_abs (f x)) + filter_upwards [h1, h2, condExp_neg f m] with a ha hb hc + exact abs_le'.2 ⟨ha, hc.symm.le.trans hb⟩ + +theorem integral_abs_condExp_le (f : α → E) : ∫ x, |μ[f | m] x| ∂μ ≤ ∫ x, |f x| ∂μ := by + by_cases! hm : ¬ m ≤ m0 + · simpa [condExp_of_not_le hm] using integral_nonneg (fun x => abs_nonneg f x) + by_cases! hsig : ¬ SigmaFinite (μ.trim hm) + · simpa [condExp_of_not_sigmaFinite hm hsig] using integral_nonneg (fun x => abs_nonneg f x) calc - eLpNorm (μ[f | m]) 1 μ ≤ eLpNorm (μ[(|f|) | m]) 1 μ := by - refine eLpNorm_mono_ae ?_ - filter_upwards [condExp_mono hf hf.abs - (ae_of_all μ (fun x => le_abs_self (f x) : ∀ x, f x ≤ |f x|)), - (condExp_neg ..).symm.le.trans (condExp_mono hf.neg hf.abs - (ae_of_all μ (fun x => neg_le_abs (f x) : ∀ x, -f x ≤ |f x|)))] with x hx₁ hx₂ - exact abs_le_abs hx₁ hx₂ - _ = eLpNorm f 1 μ := by - rw [eLpNorm_one_eq_lintegral_enorm, eLpNorm_one_eq_lintegral_enorm, - ← ENNReal.toReal_eq_toReal_iff' (hasFiniteIntegral_iff_enorm.mp integrable_condExp.2).ne - (hasFiniteIntegral_iff_enorm.mp hf.2).ne, - ← integral_norm_eq_lintegral_enorm - (stronglyMeasurable_condExp.mono hm).aestronglyMeasurable, - ← integral_norm_eq_lintegral_enorm hf.1] - simp_rw [Real.norm_eq_abs] - rw (config := { occs := .pos [2] }) [← integral_condExp hm] - refine integral_congr_ae ?_ - have : 0 ≤ᵐ[μ] μ[(|f|) | m] := by - rw [← condExp_zero] - exact condExp_mono (integrable_zero _ _ _) hf.abs - (ae_of_all μ (fun x => abs_nonneg (f x) : ∀ x, 0 ≤ |f x|)) - filter_upwards [this] with x hx - exact abs_eq_self.2 hx - -theorem integral_abs_condExp_le (f : α → ℝ) : ∫ x, |(μ[f | m]) x| ∂μ ≤ ∫ x, |f x| ∂μ := by - by_cases hm : m ≤ m0 - swap - · simp_rw [condExp_of_not_le hm, Pi.zero_apply, abs_zero, integral_zero] - positivity - by_cases hfint : Integrable f μ - swap - · simp only [condExp_of_not_integrable hfint, Pi.zero_apply, abs_zero, integral_const, - smul_eq_mul, mul_zero] - positivity - rw [integral_eq_lintegral_of_nonneg_ae, integral_eq_lintegral_of_nonneg_ae] - · apply ENNReal.toReal_mono <;> simp_rw [← Real.norm_eq_abs, ofReal_norm] - · exact hfint.2.ne - · rw [← eLpNorm_one_eq_lintegral_enorm, ← eLpNorm_one_eq_lintegral_enorm] - exact eLpNorm_one_condExp_le_eLpNorm _ - · filter_upwards with x using abs_nonneg _ - · simp_rw [← Real.norm_eq_abs] - exact hfint.1.norm - · filter_upwards with x using abs_nonneg _ - · simp_rw [← Real.norm_eq_abs] - exact (stronglyMeasurable_condExp.mono hm).aestronglyMeasurable.norm + _ ≤ ∫ x, μ[|f| | m] x ∂μ := + integral_mono_ae integrable_condExp.abs integrable_condExp (abs_condExp_ae_le_condExp_abs f) + _ = _ := integral_condExp hm -theorem setIntegral_abs_condExp_le {s : Set α} (hs : MeasurableSet[m] s) (f : α → ℝ) : - ∫ x in s, |(μ[f | m]) x| ∂μ ≤ ∫ x in s, |f x| ∂μ := by - by_cases hnm : m ≤ m0 - swap - · simp_rw [condExp_of_not_le hnm, Pi.zero_apply, abs_zero, integral_zero] - positivity - by_cases hfint : Integrable f μ - swap - · simp only [condExp_of_not_integrable hfint, Pi.zero_apply, abs_zero, integral_const, - smul_eq_mul, mul_zero] - positivity - have : ∫ x in s, |(μ[f | m]) x| ∂μ = ∫ x, |(μ[s.indicator f | m]) x| ∂μ := by - rw [← integral_indicator (hnm _ hs)] - refine integral_congr_ae ?_ - have : (fun x => |(μ[s.indicator f | m]) x|) =ᵐ[μ] fun x => |s.indicator (μ[f | m]) x| := - (condExp_indicator hfint hs).fun_comp abs - refine EventuallyEq.trans (Eventually.of_forall fun x => ?_) this.symm - rw [← Real.norm_eq_abs, norm_indicator_eq_indicator_norm] - simp only [Real.norm_eq_abs] - rw [this, ← integral_indicator (hnm _ hs)] - refine (integral_abs_condExp_le _).trans - (le_of_eq <| integral_congr_ae <| Eventually.of_forall fun x => ?_) - simp_rw [← Real.norm_eq_abs, norm_indicator_eq_indicator_norm] +/-- Note that this is not trivial as we don't assume that `f` is integrable. -/ +lemma integral_condExp_le_of_ae_nonneg {f : α → ℝ} (hf : 0 ≤ᵐ[μ] f) : + ∫ x, μ[f | m] x ∂μ ≤ ∫ x, f x ∂μ := calc + ∫ x, μ[f | m] x ∂μ = ∫ x, |μ[f | m] x| ∂μ := by + apply integral_congr_ae + filter_upwards [condExp_nonneg hf] with ω hω using (abs_of_nonneg hω).symm + _ ≤ ∫ x, |f x| ∂μ := integral_abs_condExp_le f + _ = ∫ x, f x ∂μ := by + apply integral_congr_ae + filter_upwards [hf] with ω hω using abs_of_nonneg hω + +theorem setIntegral_abs_condExp_le {s : Set α} (hs : MeasurableSet[m] s) (f : α → E) : + ∫ x in s, |μ[f | m] x| ∂μ ≤ ∫ x in s, |f x| ∂μ := by + by_cases! hm : ¬ m ≤ m0 + · simpa [condExp_of_not_le hm] using integral_nonneg (fun x => abs_nonneg f x) + by_cases! hfint : ¬ Integrable f μ + · simpa [condExp_of_not_integrable hfint] using integral_nonneg (fun x => abs_nonneg f x) + by_cases! hsig : ¬ SigmaFinite (μ.trim hm) + · simpa [condExp_of_not_sigmaFinite hm hsig] using integral_nonneg (fun x => abs_nonneg f x) + calc + _ = ∫ x in s, |(μ.restrict s)[f | m] x| ∂μ := + (integral_congr_ae ((condExp_restrict_ae_eq_restrict hm hs hfint).fun_comp abs)).symm + _ ≤ _ := integral_abs_condExp_le f + +/-- Note that this is not trivial as we don't assume that `f` is integrable. -/ +lemma setIntegral_condExp_le_of_ae_restrict_nonneg {s : Set α} (hs : MeasurableSet[m] s) {f : α → ℝ} + (hf : 0 ≤ᵐ[μ.restrict s] f) : + ∫ x in s, μ[f | m] x ∂μ ≤ ∫ x in s, f x ∂μ := by + by_cases! hm : ¬ m ≤ m0 + · simpa [condExp_of_not_le hm] using setIntegral_nonneg_of_ae_restrict hf + by_cases! hfint : ¬ Integrable f μ + · simpa [condExp_of_not_integrable hfint] using setIntegral_nonneg_of_ae_restrict hf + by_cases! hsig : ¬ SigmaFinite (μ.trim hm) + · simpa [condExp_of_not_sigmaFinite hm hsig] using setIntegral_nonneg_of_ae_restrict hf + calc + ∫ x in s, μ[f | m] x ∂μ = ∫ x in s, (μ.restrict s)[f | m] x ∂μ := + integral_congr_ae (condExp_restrict_ae_eq_restrict hm hs hfint).symm + _ ≤ ∫ x in s, f x ∂μ := integral_condExp_le_of_ae_nonneg hf + +lemma setIntegral_condExp_le_of_ae_nonneg {s : Set α} (hs : MeasurableSet[m] s) {f : α → ℝ} + (hf : 0 ≤ᵐ[μ] f) : + ∫ x in s, μ[f | m] x ∂μ ≤ ∫ x in s, f x ∂μ := + setIntegral_condExp_le_of_ae_restrict_nonneg hs (ae_restrict_le hf) + +/-- If `|f|` is bounded almost everywhere by `R`, then so is its conditional expectation. -/ +theorem ae_bdd_abs_condExp_of_ae_bdd_abs {R : E} {f : α → E} (hbdd : ∀ᵐ x ∂μ, |f x| ≤ R) : + ∀ᵐ x ∂μ, |μ[f | m] x| ≤ R := by + by_cases! hn : {x | |f x| ≤ R} = ∅ + · exact measure_mono_null (by simp) <| ae_eq_empty.1 (hn ▸ (ae_eq_univ.2 hbdd).symm) + have hR : 0 ≤ R := (abs_nonneg _).trans hn.some_mem + exact (abs_condExp_ae_le_condExp_abs f).trans (condExp_le_nonneg_const (m := m) hR hbdd) /-- If the real-valued function `f` is bounded almost everywhere by `R`, then so is its conditional expectation. -/ +@[deprecated ae_bdd_abs_condExp_of_ae_bdd_abs (since := "2026-05-05")] theorem ae_bdd_condExp_of_ae_bdd {R : ℝ≥0} {f : α → ℝ} (hbdd : ∀ᵐ x ∂μ, |f x| ≤ R) : ∀ᵐ x ∂μ, |(μ[f | m]) x| ≤ R := by by_cases hnm : m ≤ m0 @@ -169,6 +187,120 @@ theorem ae_bdd_condExp_of_ae_bdd {R : ℝ≥0} {f : α → ℝ} (hbdd : ∀ᵐ x rw [enorm_eq_nnnorm, enorm_eq_nnnorm, ENNReal.coe_le_coe, Real.nnnorm_of_nonneg R.coe_nonneg] exact Subtype.mk_le_mk.2 (le_of_lt hx) +end HasSolidNorm + +section NormedSpace + +variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E] + +theorem integral_norm_condExp_rpow_le {p : ℝ} (hp : 1 ≤ p) {f : α → E} + (hf : Integrable (‖f ·‖ ^ p) μ) : + ∫ x, ‖μ[f | m] x‖ ^ p ∂μ ≤ ∫ x, ‖f x‖ ^ p ∂μ := calc + _ ≤ ∫ x, μ[(fun x => ‖f x‖ ^ p) | m] x ∂μ := by + refine integral_mono_of_nonneg ?_ integrable_condExp (Integrable.norm_condExp_rpow_le hp hf) + filter_upwards with a using by positivity + _ ≤ _ := by + apply integral_condExp_le_of_ae_nonneg + filter_upwards with ω using by positivity + +theorem integral_norm_condExp_le (f : α → E) : ∫ x, ‖μ[f | m] x‖ ∂μ ≤ ∫ x, ‖f x‖ ∂μ := by + by_cases! hfint : ¬ Integrable f μ + · simpa [condExp_of_not_integrable hfint] using integral_nonneg (fun x => norm_nonneg (f x)) + simpa using integral_norm_condExp_rpow_le le_rfl (by simpa using hfint.norm) + +theorem setIntegral_norm_condExp_rpow_le {p : ℝ} (hp : 1 ≤ p) {f : α → E} {s : Set α} + (hs : MeasurableSet[m] s) (hf : Integrable (‖f ·‖ ^ p) μ) : + ∫ x in s, ‖μ[f | m] x‖ ^ p ∂μ ≤ ∫ x in s, ‖f x‖ ^ p ∂μ := by + have hp' : p ≠ 0 := by linarith + by_cases! hm : ¬ m ≤ m0 + · simpa [condExp_of_not_le hm, hp'] using integral_nonneg (fun x => by positivity) + by_cases! hsig : ¬ SigmaFinite (μ.trim hm) + · simpa [condExp_of_not_sigmaFinite hm hsig, hp'] using integral_nonneg (fun x => by positivity) + calc + _ ≤ ∫ x in s, μ[(fun x => ‖f x‖ ^ p) | m] x ∂μ := by + refine integral_mono_of_nonneg ?_ ?_ ?_ + · filter_upwards with a using by positivity + · exact integrable_condExp.congr (condExp_restrict_ae_eq_restrict hm hs hf) + · exact ae_restrict_le (Integrable.norm_condExp_rpow_le hp hf) + _ ≤ _ := by + apply setIntegral_condExp_le_of_ae_nonneg hs + filter_upwards with ω using by positivity + +theorem setIntegral_norm_condExp_le {s : Set α} (hs : MeasurableSet[m] s) (f : α → E) : + ∫ x in s, ‖(μ[f | m]) x‖ ∂μ ≤ ∫ x in s, ‖f x‖ ∂μ := by + by_cases! hfint : ¬ Integrable f μ + · simpa [condExp_of_not_integrable hfint] using integral_nonneg (fun x => norm_nonneg (f x)) + simpa using setIntegral_norm_condExp_rpow_le le_rfl hs (by simpa using hfint.norm) + +/-- If `‖f‖` is bounded almost everywhere by `R`, then so is its conditional expectation. -/ +theorem ae_bdd_norm_condExp_of_ae_bdd_norm {R : ℝ} {f : α → E} (hbdd : ∀ᵐ x ∂μ, ‖f x‖ ≤ R) : + ∀ᵐ x ∂μ, ‖μ[f | m] x‖ ≤ R := by + by_cases! hn : {x | ‖f x‖ ≤ R} = ∅ + · exact measure_mono_null (by simp) <| ae_eq_empty.1 (hn ▸ (ae_eq_univ.2 hbdd).symm) + exact (norm_condExp_le f).trans (condExp_le_nonneg_const ((norm_nonneg _).trans hn.some_mem) hbdd) + +theorem MemLp.ae_norm_condExp_le_essSup {f : α → E} (hf : MemLp f ∞ μ) : + ∀ᵐ (x : α) ∂μ, ‖μ[f | m] x‖ ≤ essSup (‖f ·‖) μ := + ae_bdd_norm_condExp_of_ae_bdd_norm (ae_le_essSup ⟨_, ae_le_lpNorm_exponent_top hf⟩) + +theorem MemLp.essSup_norm_condExp_le_essSup_norm [NeZero μ] {f : α → E} (hf : MemLp f ∞ μ) : + essSup (fun x ↦ ‖μ[f | m] x‖) μ ≤ essSup (fun x ↦ ‖f x‖) μ := + essSup_le_of_ae_le _ hf.ae_norm_condExp_le_essSup + (isCoboundedUnder_le_of_le _ (fun _ => norm_nonneg _)) + +theorem MemLp.lpNorm_condExp_le_lpNorm {f : α → E} {p : ℝ≥0∞} (hp : 1 ≤ p) (hf : MemLp f p μ) : + lpNorm μ[f | m] p μ ≤ lpNorm f p μ := by + by_cases NeZero μ + · have hp' : 0 < p := zero_lt_one.trans_le hp + by_cases! hm : ¬ m ≤ m0 + · simp [condExp_of_not_le hm] + by_cases! hsig : ¬ SigmaFinite (μ.trim hm) + · simp [condExp_of_not_sigmaFinite hm hsig] + · by_cases! hpt : p ≠ ⊤ + · rw [lpNorm_eq_integral_norm_rpow_toReal hp'.ne.symm hpt hf.1, + lpNorm_eq_integral_norm_rpow_toReal hp'.ne.symm hpt integrable_condExp.1] + gcongr ?_ ^ ?_ + have : 1 ≤ p.toReal := by + rwa [← ENNReal.toReal_one, ENNReal.toReal_le_toReal ENNReal.one_ne_top hpt] + exact integral_norm_condExp_rpow_le this <| + (integrable_norm_rpow_iff hf.1 hp'.ne.symm hpt).2 hf + · by_cases! h : MemLp μ[f | m] ⊤ μ + · simp_all only [lpNorm_exponent_top_eq_essSup] + exact hf.essSup_norm_condExp_le_essSup_norm + · simp_all + · simp_all [not_neZero] + +theorem MemLp.condExp {f : α → E} {p : ℝ≥0∞} (hp : 1 ≤ p) (hf : MemLp f p μ) : + MemLp (μ[f | m]) p μ := by + have hp' : 0 < p := zero_lt_one.trans_le hp + by_cases! hpt : p ≠ ⊤ + · rw [← integrable_norm_rpow_iff integrable_condExp.1 hp'.ne.symm hpt] + have hp : 1 ≤ p.toReal := by + rwa [← ENNReal.toReal_one, ENNReal.toReal_le_toReal ENNReal.one_ne_top hpt] + have := Integrable.norm_condExp_rpow_le (m := m) hp <| + (integrable_norm_rpow_iff hf.1 hp'.ne.symm hpt).2 hf + refine Integrable.mono_nonneg integrable_condExp ?_ ?_ this + · fun_prop (discharger := simp) + · filter_upwards with a; positivity + · simp_all only + exact memLp_top_of_bound integrable_condExp.1 (essSup (‖f ·‖) μ) hf.ae_norm_condExp_le_essSup + +theorem eLpNorm_condExp_le_eLpNorm (f : α → E) {p : ℝ≥0∞} (hp : 1 ≤ p) : + eLpNorm (μ[f | m]) p μ ≤ eLpNorm f p μ := by + by_cases! hf : MemLp f p μ + · rw [← ofReal_lpNorm hf, ← ofReal_lpNorm (hf.condExp hp)] + exact ENNReal.ofReal_le_ofReal (hf.lpNorm_condExp_le_lpNorm hp) + · simp only [MemLp, not_and, not_lt, top_le_iff] at hf + by_cases! ha : AEStronglyMeasurable f μ + · simp [hf ha] + · simp [condExp_of_not_integrable (fun h => ha h.aestronglyMeasurable)] + +@[deprecated eLpNorm_condExp_le_eLpNorm (since := "2026-07-01")] +theorem eLpNorm_one_condExp_le_eLpNorm (f : α → E) : eLpNorm (μ[f | m]) 1 μ ≤ eLpNorm f 1 μ := + eLpNorm_condExp_le_eLpNorm f (refl 1) + +end NormedSpace + /-- Given an integrable function `g`, the conditional expectations of `g` with respect to a sequence of sub-σ-algebras is uniformly integrable. -/ theorem Integrable.uniformIntegrable_condExp {ι : Type*} [IsFiniteMeasure μ] {g : α → ℝ} @@ -209,12 +341,12 @@ theorem Integrable.uniformIntegrable_condExp {ι : Type*} [IsFiniteMeasure μ] { hC, NNReal.inv_mk, ENNReal.coe_mul, ENNReal.coe_toNNReal hg.eLpNorm_lt_top.ne, ← mul_assoc, ← ENNReal.ofReal_eq_coe_nnreal, ← ENNReal.ofReal_mul hδ.le, mul_inv_cancel₀ hδ.ne', ENNReal.ofReal_one, one_mul, ENNReal.rpow_one] - exact eLpNorm_one_condExp_le_eLpNorm _ + exact eLpNorm_condExp_le_eLpNorm _ le_rfl refine ⟨C, fun n => le_trans ?_ (h {x : α | C ≤ ‖(μ[g|ℱ n]) x‖₊} (hmeas n C) (this n))⟩ have hmeasℱ : MeasurableSet[ℱ n] {x : α | C ≤ ‖(μ[g|ℱ n]) x‖₊} := @measurableSet_le _ _ _ _ _ (ℱ n) _ _ _ _ _ measurable_const (@Measurable.nnnorm _ _ _ _ _ (ℱ n) _ stronglyMeasurable_condExp.measurable) rw [← eLpNorm_congr_ae (condExp_indicator hint hmeasℱ)] - exact eLpNorm_one_condExp_le_eLpNorm _ + exact eLpNorm_condExp_le_eLpNorm _ le_rfl end MeasureTheory diff --git a/Mathlib/MeasureTheory/Function/ConditionalLExpectation.lean b/Mathlib/MeasureTheory/Function/ConditionalLExpectation.lean index dc91e30d562b2c..4e7c80eed2058a 100644 --- a/Mathlib/MeasureTheory/Function/ConditionalLExpectation.lean +++ b/Mathlib/MeasureTheory/Function/ConditionalLExpectation.lean @@ -9,7 +9,6 @@ public import Mathlib.MeasureTheory.Measure.Decomposition.Lebesgue import Mathlib.MeasureTheory.Measure.Decomposition.RadonNikodym import Mathlib.Probability.Notation -public import Mathlib.Probability.Notation /-! # Conditional Lebesgue expectation diff --git a/Mathlib/MeasureTheory/Function/ContinuousMapDense.lean b/Mathlib/MeasureTheory/Function/ContinuousMapDense.lean index 347078a2082a36..81038233b5104e 100644 --- a/Mathlib/MeasureTheory/Function/ContinuousMapDense.lean +++ b/Mathlib/MeasureTheory/Function/ContinuousMapDense.lean @@ -5,8 +5,6 @@ Authors: Heather Macbeth -/ module -public import Mathlib.MeasureTheory.Measure.Regular -public import Mathlib.MeasureTheory.Function.SimpleFuncDenseLp public import Mathlib.Topology.UrysohnsLemma public import Mathlib.MeasureTheory.Function.LpSpace.ContinuousFunctions public import Mathlib.MeasureTheory.Integral.Bochner.Basic diff --git a/Mathlib/MeasureTheory/Function/EssSup.lean b/Mathlib/MeasureTheory/Function/EssSup.lean index 8e9f3f7228b0e8..62d7b80de3e784 100644 --- a/Mathlib/MeasureTheory/Function/EssSup.lean +++ b/Mathlib/MeasureTheory/Function/EssSup.lean @@ -5,13 +5,12 @@ Authors: Rémy Degenne -/ module -public import Mathlib.MeasureTheory.Constructions.BorelSpace.Order -public import Mathlib.MeasureTheory.Measure.Count public import Mathlib.Order.Filter.ENNReal public import Mathlib.Probability.UniformOn /-! # Essential supremum and infimum + We define the essential supremum and infimum of a function `f : α → β` with respect to a measure `μ` on `α`. The essential supremum is the infimum of the constants `c : β` such that `f x ≤ c` almost everywhere. diff --git a/Mathlib/MeasureTheory/Function/FactorsThrough.lean b/Mathlib/MeasureTheory/Function/FactorsThrough.lean index ac036aed412880..11d9a0fc7c5160 100644 --- a/Mathlib/MeasureTheory/Function/FactorsThrough.lean +++ b/Mathlib/MeasureTheory/Function/FactorsThrough.lean @@ -5,7 +5,6 @@ Authors: Etienne Marion -/ module -public import Mathlib.MeasureTheory.Constructions.Polish.StronglyMeasurable public import Mathlib.Probability.Process.Filtration /-! diff --git a/Mathlib/MeasureTheory/Function/Jacobian.lean b/Mathlib/MeasureTheory/Function/Jacobian.lean index e18fc62dd59fa7..4df52b8c00a28a 100644 --- a/Mathlib/MeasureTheory/Function/Jacobian.lean +++ b/Mathlib/MeasureTheory/Function/Jacobian.lean @@ -8,11 +8,7 @@ module public import Mathlib.Analysis.Calculus.FDeriv.Congr public import Mathlib.MeasureTheory.Constructions.BorelSpace.ContinuousLinearMap public import Mathlib.MeasureTheory.Covering.BesicovitchVectorSpace -public import Mathlib.MeasureTheory.Measure.Lebesgue.EqHaar -public import Mathlib.Analysis.Normed.Module.Ball.Pointwise -public import Mathlib.MeasureTheory.Constructions.Polish.Basic public import Mathlib.Analysis.Calculus.InverseFunctionTheorem.ApproximatesLinearOn -public import Mathlib.Topology.Algebra.Module.Determinant /-! # Change of variables in higher-dimensional integrals diff --git a/Mathlib/MeasureTheory/Function/L2Space.lean b/Mathlib/MeasureTheory/Function/L2Space.lean index 4f5c79ce5e14dc..8431ba00bd432c 100644 --- a/Mathlib/MeasureTheory/Function/L2Space.lean +++ b/Mathlib/MeasureTheory/Function/L2Space.lean @@ -6,8 +6,6 @@ Authors: Rémy Degenne module public import Mathlib.Analysis.InnerProductSpace.GramMatrix -public import Mathlib.Analysis.InnerProductSpace.LinearMap -public import Mathlib.Analysis.RCLike.Lemmas public import Mathlib.MeasureTheory.Function.LpSpace.ContinuousFunctions public import Mathlib.MeasureTheory.Function.StronglyMeasurable.Inner public import Mathlib.MeasureTheory.Integral.Bochner.ContinuousLinearMap diff --git a/Mathlib/MeasureTheory/Function/LpOrder.lean b/Mathlib/MeasureTheory/Function/LpOrder.lean index e28f57d227f38a..c846924343ac54 100644 --- a/Mathlib/MeasureTheory/Function/LpOrder.lean +++ b/Mathlib/MeasureTheory/Function/LpOrder.lean @@ -5,9 +5,7 @@ Authors: Rémy Degenne -/ module -public import Mathlib.Analysis.Normed.Order.Lattice public import Mathlib.MeasureTheory.Function.ConvergenceInMeasure -public import Mathlib.MeasureTheory.Function.LpSpace.Basic /-! # Order related properties of Lp spaces diff --git a/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean b/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean index 96fc1608bff08b..f22688a5976fb8 100644 --- a/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean +++ b/Mathlib/MeasureTheory/Function/LpSeminorm/Basic.lean @@ -9,7 +9,6 @@ public import Mathlib.Data.Fintype.Order public import Mathlib.MeasureTheory.Function.AEEqFun public import Mathlib.MeasureTheory.Function.LpSeminorm.Defs public import Mathlib.MeasureTheory.Function.SpecialFunctions.Basic -public import Mathlib.MeasureTheory.Integral.Lebesgue.Countable /-! # Basic theorems about ℒp space @@ -285,17 +284,17 @@ theorem eLpNorm'_mono_ae {f : α → F} {g : α → G} (hq : 0 ≤ q) (h : ∀ eLpNorm' f q μ ≤ eLpNorm' g q μ := eLpNorm'_mono_enorm_ae hq (by simpa only [enorm_le_iff_norm_le] using h) -theorem eLpNorm'_congr_enorm_ae {f g : α → ε} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ = ‖g x‖ₑ) : +theorem eLpNorm'_congr_enorm_ae {f : α → ε} {g : α → ε'} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ = ‖g x‖ₑ) : eLpNorm' f q μ = eLpNorm' g q μ := by have : (‖f ·‖ₑ ^ q) =ᵐ[μ] (‖g ·‖ₑ ^ q) := hfg.mono fun x hx ↦ by simp [hx] simp only [eLpNorm'_eq_lintegral_enorm, lintegral_congr_ae this] -theorem eLpNorm'_congr_nnnorm_ae {f g : α → F} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ = ‖g x‖₊) : +theorem eLpNorm'_congr_nnnorm_ae {f : α → F} {g : α → G} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ = ‖g x‖₊) : eLpNorm' f q μ = eLpNorm' g q μ := by have : (‖f ·‖ₑ ^ q) =ᵐ[μ] (‖g ·‖ₑ ^ q) := hfg.mono fun x hx ↦ by simp [enorm, hx] simp only [eLpNorm'_eq_lintegral_enorm, lintegral_congr_ae this] -theorem eLpNorm'_congr_norm_ae {f g : α → F} (hfg : ∀ᵐ x ∂μ, ‖f x‖ = ‖g x‖) : +theorem eLpNorm'_congr_norm_ae {f : α → F} {g : α → G} (hfg : ∀ᵐ x ∂μ, ‖f x‖ = ‖g x‖) : eLpNorm' f q μ = eLpNorm' g q μ := eLpNorm'_congr_nnnorm_ae <| hfg.mono fun _x hx => NNReal.eq hx @@ -306,11 +305,11 @@ theorem eLpNormEssSup_congr_ae {f g : α → ε} (hfg : f =ᵐ[μ] g) : eLpNormEssSup f μ = eLpNormEssSup g μ := essSup_congr_ae (hfg.fun_comp enorm) -theorem eLpNormEssSup_mono_enorm_ae {f g : α → ε} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) : +theorem eLpNormEssSup_mono_enorm_ae {f : α → ε} {g : α → ε'} (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) : eLpNormEssSup f μ ≤ eLpNormEssSup g μ := essSup_mono_ae <| hfg -theorem eLpNormEssSup_mono_nnnorm_ae {f g : α → F} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ ≤ ‖g x‖₊) : +theorem eLpNormEssSup_mono_nnnorm_ae {f : α → F} {g : α → G} (hfg : ∀ᵐ x ∂μ, ‖f x‖₊ ≤ ‖g x‖₊) : eLpNormEssSup f μ ≤ eLpNormEssSup g μ := essSup_mono_ae <| hfg.mono fun _x hx => ENNReal.coe_le_coe.mpr hx @@ -319,7 +318,7 @@ theorem eLpNorm_mono_enorm_ae {f : α → ε} {g : α → ε'} (h : ∀ᵐ x ∂ simp only [eLpNorm] split_ifs · exact le_rfl - · exact essSup_mono_ae h + · exact eLpNormEssSup_mono_enorm_ae h · exact eLpNorm'_mono_enorm_ae ENNReal.toReal_nonneg h theorem eLpNorm_mono_nnnorm_ae {f : α → F} {g : α → G} (h : ∀ᵐ x ∂μ, ‖f x‖₊ ≤ ‖g x‖₊) : @@ -330,10 +329,7 @@ theorem eLpNorm_mono_ae {f : α → F} {g : α → G} (h : ∀ᵐ x ∂μ, ‖f eLpNorm f p μ ≤ eLpNorm g p μ := eLpNorm_mono_enorm_ae (by simpa only [enorm_le_iff_norm_le] using h) -theorem eLpNorm_mono_ae' {ε' : Type*} [ENorm ε'] - {f : α → ε} {g : α → ε'} (h : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) : - eLpNorm f p μ ≤ eLpNorm g p μ := - eLpNorm_mono_enorm_ae (by simpa only [enorm_le_iff_norm_le] using h) +@[deprecated (since := "2026-06-24")] alias eLpNorm_mono_ae' := eLpNorm_mono_enorm_ae theorem eLpNorm_mono_ae_real {f : α → F} {g : α → ℝ} (h : ∀ᵐ x ∂μ, ‖f x‖ ≤ g x) : eLpNorm f p μ ≤ eLpNorm g p μ := @@ -501,7 +497,7 @@ variable {ε ε' : Type*} theorem MemLp.of_le_enorm {f : α → ε} {g : α → ε'} (hg : MemLp g p μ) (hf : AEStronglyMeasurable f μ) (hfg : ∀ᵐ x ∂μ, ‖f x‖ₑ ≤ ‖g x‖ₑ) : MemLp f p μ := - ⟨hf, (eLpNorm_mono_ae' hfg).trans_lt (by finiteness)⟩ + ⟨hf, (eLpNorm_mono_enorm_ae hfg).trans_lt (by finiteness)⟩ theorem MemLp.of_le {f : α → E} {g : α → F} (hg : MemLp g p μ) (hf : AEStronglyMeasurable f μ) (hfg : ∀ᵐ x ∂μ, ‖f x‖ ≤ ‖g x‖) : MemLp f p μ := @@ -661,6 +657,12 @@ theorem eLpNorm_smul_measure_of_ne_zero {c : ℝ≥0∞} (hc : c ≠ 0) (f : α · simp [*] exact eLpNorm_smul_measure_of_ne_zero_of_ne_top hp0 hp_top c +theorem eLpNorm_smul_measure_le (c : ℝ≥0∞) (f : α → ε) (p : ℝ≥0∞) (μ : Measure α) : + eLpNorm f p (c • μ) ≤ c ^ (1 / p).toReal • eLpNorm f p μ := by + rcases eq_or_ne c 0 with rfl | hc + · simp + · exact (eLpNorm_smul_measure_of_ne_zero hc f p μ).le + /-- See `eLpNorm_smul_measure_of_ne_zero` for a version with scalar multiplication by `ℝ≥0∞`. -/ lemma eLpNorm_smul_measure_of_ne_zero' {c : ℝ≥0} (hc : c ≠ 0) (f : α → ε) (p : ℝ≥0∞) (μ : Measure α) : eLpNorm f p (c • μ) = c ^ p.toReal⁻¹ • eLpNorm f p μ := @@ -684,15 +686,16 @@ theorem eLpNorm_one_smul_measure {f : α → ε} (c : ℝ≥0∞) : eLpNorm f 1 (c • μ) = c * eLpNorm f 1 μ := by rw [eLpNorm_smul_measure_of_ne_top] <;> simp +theorem eLpNorm_le_of_measure_le_smul {c : ℝ≥0∞} + {μ μ' : Measure α} (h : μ' ≤ c • μ) {f : α → ε} {p : ℝ≥0∞} : + eLpNorm f p μ' ≤ c ^ (1 / p).toReal • eLpNorm f p μ := by + grw [eLpNorm_mono_measure f h, eLpNorm_smul_measure_le] + theorem MemLp.of_measure_le_smul {μ' : Measure α} {c : ℝ≥0∞} (hc : c ≠ ∞) (hμ'_le : μ' ≤ c • μ) {f : α → ε} (hf : MemLp f p μ) : MemLp f p μ' := by refine ⟨hf.1.mono_ac (Measure.absolutelyContinuous_of_le_smul hμ'_le), ?_⟩ - refine (eLpNorm_mono_measure f hμ'_le).trans_lt ?_ - by_cases hc0 : c = 0 - · simp [hc0] - rw [eLpNorm_smul_measure_of_ne_zero hc0, smul_eq_mul] - refine ENNReal.mul_lt_top (Ne.lt_top ?_) hf.2 - simp [hc, hc0] + grw [eLpNorm_le_of_measure_le_smul hμ'_le] + exact ENNReal.mul_lt_top (Ne.lt_top (by simp [hc])) hf.2 theorem MemLp.smul_measure {f : α → ε} {c : ℝ≥0∞} (hf : MemLp f p μ) (hc : c ≠ ∞) : MemLp f p (c • μ) := @@ -762,8 +765,8 @@ theorem eLpNorm_eq_zero_of_ae_zero {f : α → ε} (hf : f =ᵐ[μ] 0) : eLpNorm theorem eLpNorm'_eq_zero_of_ae_eq_zero {f : α → ε} {p : ℝ} (hp : 0 < p) (hf : ∀ᵐ (x : α) ∂μ, ‖f x‖ₑ = 0) : eLpNorm' f p μ = 0 := by - rw [← eLpNorm'_zero hp (μ := μ) (ε := ε), eLpNorm'_congr_enorm_ae] - simp only [hf, Pi.zero_apply, enorm_zero] + rw [← eLpNorm'_zero hp (μ := μ) (ε := ε)] + exact eLpNorm'_congr_enorm_ae (by simp [hf]) variable {ε : Type*} [ENorm ε] in theorem ae_le_eLpNormEssSup {f : α → ε} : ∀ᵐ y ∂μ, ‖f y‖ₑ ≤ eLpNormEssSup f μ := diff --git a/Mathlib/MeasureTheory/Function/LpSeminorm/CompareExp.lean b/Mathlib/MeasureTheory/Function/LpSeminorm/CompareExp.lean index bef5c5cfd5ee27..876635b9437d6d 100644 --- a/Mathlib/MeasureTheory/Function/LpSeminorm/CompareExp.lean +++ b/Mathlib/MeasureTheory/Function/LpSeminorm/CompareExp.lean @@ -5,7 +5,6 @@ Authors: Rémy Degenne, Eric Wieser -/ module -public import Mathlib.Data.ENNReal.Holder public import Mathlib.MeasureTheory.Function.LpSeminorm.Indicator public import Mathlib.MeasureTheory.Function.LpSeminorm.SMul public import Mathlib.MeasureTheory.Integral.MeanInequalities diff --git a/Mathlib/MeasureTheory/Function/LpSeminorm/Defs.lean b/Mathlib/MeasureTheory/Function/LpSeminorm/Defs.lean index 3149a3d292d58f..1bf1d4daa46fff 100644 --- a/Mathlib/MeasureTheory/Function/LpSeminorm/Defs.lean +++ b/Mathlib/MeasureTheory/Function/LpSeminorm/Defs.lean @@ -8,7 +8,6 @@ module public import Mathlib.Analysis.SpecialFunctions.Pow.NNReal public import Mathlib.MeasureTheory.Function.EssSup public import Mathlib.MeasureTheory.Function.StronglyMeasurable.AEStronglyMeasurable -public import Mathlib.MeasureTheory.Integral.Lebesgue.Basic /-! # ℒp space diff --git a/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean b/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean index 7a4da8882b40b4..4bfab0e33c6d3a 100644 --- a/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean +++ b/Mathlib/MeasureTheory/Function/LpSeminorm/Indicator.lean @@ -60,13 +60,13 @@ lemma eLpNorm_restrict_le (f : α → ε') (p : ℝ≥0∞) (μ : Measure α) (s lemma eLpNorm_indicator_le (f : α → ε) : eLpNorm (s.indicator f) p μ ≤ eLpNorm f p μ := by - refine eLpNorm_mono_ae' <| .of_forall fun x ↦ ?_ - rw [enorm_indicator_eq_indicator_enorm] - exact s.indicator_le_self _ x + apply eLpNorm_mono_enorm + simp_rw [enorm_indicator_eq_indicator_enorm] + exact s.indicator_le_self _ lemma eLpNormEssSup_indicator_le (s : Set α) (f : α → ε) : eLpNormEssSup (s.indicator f) μ ≤ eLpNormEssSup f μ := by - refine essSup_mono_ae (Eventually.of_forall fun x => ?_) + refine essSup_mono_ae (.of_forall fun x => ?_) simp_rw [enorm_indicator_eq_indicator_enorm] exact Set.indicator_le_self s _ x diff --git a/Mathlib/MeasureTheory/Function/LpSeminorm/Prod.lean b/Mathlib/MeasureTheory/Function/LpSeminorm/Prod.lean index 31d12b1d172ce2..61c3be1d93ee87 100644 --- a/Mathlib/MeasureTheory/Function/LpSeminorm/Prod.lean +++ b/Mathlib/MeasureTheory/Function/LpSeminorm/Prod.lean @@ -6,7 +6,6 @@ Authors: Rémy Degenne module public import Mathlib.MeasureTheory.Function.LpSeminorm.Basic -public import Mathlib.MeasureTheory.Measure.Prod /-! # ℒp spaces and products diff --git a/Mathlib/MeasureTheory/Function/LpSpace/Basic.lean b/Mathlib/MeasureTheory/Function/LpSpace/Basic.lean index f66de7c766a34e..da03fba965ce30 100644 --- a/Mathlib/MeasureTheory/Function/LpSpace/Basic.lean +++ b/Mathlib/MeasureTheory/Function/LpSpace/Basic.lean @@ -232,7 +232,6 @@ theorem nnnorm_def (f : Lp E p μ) : ‖f‖₊ = ENNReal.toNNReal (eLpNorm f p protected theorem coe_nnnorm (f : Lp E p μ) : (‖f‖₊ : ℝ) = ‖f‖ := rfl -@[simp] theorem enorm_def (f : Lp E p μ) : ‖f‖ₑ = eLpNorm f p μ := ENNReal.coe_toNNReal <| Lp.eLpNorm_ne_top f @@ -245,6 +244,7 @@ theorem nnnorm_toLp (f : α → E) (hf : MemLp f p μ) : ‖hf.toLp f‖₊ = ENNReal.toNNReal (eLpNorm f p μ) := NNReal.eq <| norm_toLp f hf +@[simp] lemma enorm_toLp {f : α → E} (hf : MemLp f p μ) : ‖hf.toLp f‖ₑ = eLpNorm f p μ := by simp_rw [enorm, nnnorm_toLp f hf, ENNReal.coe_toNNReal hf.2.ne] @@ -381,7 +381,10 @@ theorem norm_le_of_ae_bound [IsFiniteMeasure μ] {f : Lp E p μ} {C : ℝ} (hC : have := nnnorm_le_of_ae_bound hfC rwa [← NNReal.coe_le_coe, NNReal.coe_mul, NNReal.coe_rpow] at this +instance instAddCommGroup : AddCommGroup (Lp E p μ) := inferInstance + instance instNormedAddCommGroup [hp : Fact (1 ≤ p)] : NormedAddCommGroup (Lp E p μ) := + fast_instance% { AddGroupNorm.toNormedAddCommGroup { toFun := (norm : Lp E p μ → ℝ) map_zero' := norm_zero @@ -869,6 +872,66 @@ end ContinuousLinearMap namespace MeasureTheory.Lp +section LpToLpOfMeasureLeSMul + +variable [NormedSpace ℝ E] {ν : Measure α} {c : ℝ≥0∞} + +/-- The canonical map from `Lᵖ ν` to `Lᵖ μ` when `μ` is bounded by a finite multiple of `ν`. +This is the linear map version. Use instead the continuous linear map +version `LpToLpOfMeasureLeSMul` -/ +private noncomputable def LpToLpOfMeasureLeSMulₗ (hc : c ≠ ∞) (h : μ ≤ c • ν) : + Lp E p ν →ₗ[ℝ] Lp E p μ where + toFun f := ((Lp.memLp f).of_measure_le_smul hc h).toLp f + map_add' f g := by + ext + grw [MemLp.coeFn_toLp, Lp.coeFn_add, MemLp.coeFn_toLp, MemLp.coeFn_toLp] + have : μ ≪ ν := Measure.absolutelyContinuous_of_le_smul h + apply this.ae_eq + grw [Lp.coeFn_add] + map_smul' c f := by + ext + grw [MemLp.coeFn_toLp, Lp.coeFn_smul, MemLp.coeFn_toLp] + have : μ ≪ ν := Measure.absolutelyContinuous_of_le_smul h + apply this.ae_eq + grw [Lp.coeFn_smul] + rfl + +private lemma coeFn_LpToLpOfMeasureLeSMulₗ (hc : c ≠ ∞) (h : μ ≤ c • ν) (f : Lp E p ν) : + LpToLpOfMeasureLeSMulₗ hc h f =ᵐ[μ] f := by + simp [LpToLpOfMeasureLeSMulₗ, MemLp.coeFn_toLp] + +private lemma enorm_LpToLpOfMeasureLeSMulₗ_apply_le + (hc : c ≠ ∞) (h : μ ≤ c • ν) [Fact (1 ≤ p)] {f : Lp E p ν} : + ‖LpToLpOfMeasureLeSMulₗ hc h f‖ₑ ≤ c ^ (1 / p).toReal * ‖f‖ₑ := by + simp only [Lp.enorm_def] + rw [eLpNorm_congr_ae (coeFn_LpToLpOfMeasureLeSMulₗ hc h f)] + exact eLpNorm_le_of_measure_le_smul h + +private lemma norm_LpToLpOfMeasureLeSMulₗ_apply_le + (hc : c ≠ ∞) (h : μ ≤ c • ν) [Fact (1 ≤ p)] {f : Lp E p ν} : + ‖LpToLpOfMeasureLeSMulₗ hc h f‖ ≤ c.toReal ^ (1 / p).toReal * ‖f‖ := by + simp only [← toReal_enorm] + rw [ENNReal.toReal_rpow, ← ENNReal.toReal_mul] + grw [enorm_LpToLpOfMeasureLeSMulₗ_apply_le] + simp [ENNReal.mul_eq_top, hc] + +/-- The canonical map from `Lᵖ ν` to `Lᵖ μ` when `μ` is bounded by a finite multiple of `ν`. -/ +@[no_expose] +noncomputable def LpToLpOfMeasureLeSMul [Fact (1 ≤ p)] (hc : c ≠ ∞) (h : μ ≤ c • ν) : + Lp E p ν →L[ℝ] Lp E p μ := + LinearMap.mkContinuous (LpToLpOfMeasureLeSMulₗ hc h) (c.toReal ^ (1 / p).toReal) + (fun _ ↦ norm_LpToLpOfMeasureLeSMulₗ_apply_le hc h) + +lemma coeFn_LpToLpOfMeasureLeSMul [Fact (1 ≤ p)] (hc : c ≠ ∞) (h : μ ≤ c • ν) (f : Lp E p ν) : + LpToLpOfMeasureLeSMul hc h f =ᵐ[μ] f := + coeFn_LpToLpOfMeasureLeSMulₗ hc h f + +lemma norm_LpToLpOfMeasureLeSMul_le [Fact (1 ≤ p)] (hc : c ≠ ∞) (h : μ ≤ c • ν) : + ‖(LpToLpOfMeasureLeSMul hc h : Lp E p ν →L[ℝ] Lp E p μ)‖ ≤ c.toReal ^ (1 / p).toReal := + LinearMap.mkContinuous_norm_le _ (Real.rpow_nonneg (by simp) _) _ + +end LpToLpOfMeasureLeSMul + section PosPart theorem lipschitzWith_pos_part : LipschitzWith 1 fun x : ℝ => max x 0 := diff --git a/Mathlib/MeasureTheory/Function/LpSpace/CompleteOfCompleteLp.lean b/Mathlib/MeasureTheory/Function/LpSpace/CompleteOfCompleteLp.lean index 82649b61808f46..298438c512f99e 100644 --- a/Mathlib/MeasureTheory/Function/LpSpace/CompleteOfCompleteLp.lean +++ b/Mathlib/MeasureTheory/Function/LpSpace/CompleteOfCompleteLp.lean @@ -5,9 +5,6 @@ Authors: Sébastien Gouëzel -/ module -public import Mathlib.Analysis.Normed.Operator.Mul -public import Mathlib.MeasureTheory.Function.ConvergenceInMeasure -public import Mathlib.MeasureTheory.Function.LpSpace.Complete public import Mathlib.MeasureTheory.Function.StronglyMeasurable.Lp /-! diff --git a/Mathlib/MeasureTheory/Function/LpSpace/ContinuousFunctions.lean b/Mathlib/MeasureTheory/Function/LpSpace/ContinuousFunctions.lean index 787dd02b647672..890ac7da030e22 100644 --- a/Mathlib/MeasureTheory/Function/LpSpace/ContinuousFunctions.lean +++ b/Mathlib/MeasureTheory/Function/LpSpace/ContinuousFunctions.lean @@ -7,7 +7,6 @@ module public import Mathlib.Analysis.Normed.Operator.NormedSpace public import Mathlib.MeasureTheory.Function.LpSpace.Basic -public import Mathlib.MeasureTheory.Measure.OpenPos public import Mathlib.Topology.ContinuousMap.Compact /-! diff --git a/Mathlib/MeasureTheory/Function/SimpleFuncDenseLp.lean b/Mathlib/MeasureTheory/Function/SimpleFuncDenseLp.lean index b81ea770d8eefd..96eff255ca78e7 100644 --- a/Mathlib/MeasureTheory/Function/SimpleFuncDenseLp.lean +++ b/Mathlib/MeasureTheory/Function/SimpleFuncDenseLp.lean @@ -6,7 +6,6 @@ Authors: Zhouhang Zhou, Yury Kudryashov, Heather Macbeth module public import Mathlib.MeasureTheory.Function.L1Space.AEEqFun -public import Mathlib.MeasureTheory.Function.LpSpace.Complete public import Mathlib.MeasureTheory.Function.LpSpace.Indicator /-! diff --git a/Mathlib/MeasureTheory/Function/SpecialFunctions/Basic.lean b/Mathlib/MeasureTheory/Function/SpecialFunctions/Basic.lean index 53c2f9e66bc6f4..8fdb92ba8a019d 100644 --- a/Mathlib/MeasureTheory/Function/SpecialFunctions/Basic.lean +++ b/Mathlib/MeasureTheory/Function/SpecialFunctions/Basic.lean @@ -56,7 +56,7 @@ lemma aemeasurable_of_aemeasurable_exp_mul {t : ℝ} (ht : t ≠ 0) (hf : AEMeasurable (fun x ↦ exp (t * f x)) μ) : AEMeasurable f μ := by simpa only [mul_div_cancel_left₀ _ ht] - using (aemeasurable_of_aemeasurable_exp hf).div (aemeasurable_const (b := t)) + using (aemeasurable_of_aemeasurable_exp hf).fun_div (aemeasurable_const (b := t)) theorem measurable_sin : Measurable sin := continuous_sin.measurable diff --git a/Mathlib/MeasureTheory/Function/SpecialFunctions/RCLike.lean b/Mathlib/MeasureTheory/Function/SpecialFunctions/RCLike.lean index c22984209c3549..703f87f29b7afa 100644 --- a/Mathlib/MeasureTheory/Function/SpecialFunctions/RCLike.lean +++ b/Mathlib/MeasureTheory/Function/SpecialFunctions/RCLike.lean @@ -66,14 +66,14 @@ theorem RCLike.measurable_ofReal : Measurable ((↑) : ℝ → 𝕜) := theorem measurable_of_re_im (hre : Measurable fun x => RCLike.re (f x)) (him : Measurable fun x => RCLike.im (f x)) : Measurable f := by convert! - Measurable.add (M := 𝕜) (RCLike.measurable_ofReal.comp hre) + Measurable.fun_add (M := 𝕜) (RCLike.measurable_ofReal.comp hre) ((RCLike.measurable_ofReal.comp him).mul_const RCLike.I) exact (RCLike.re_add_im _).symm theorem aemeasurable_of_re_im (hre : AEMeasurable (fun x => RCLike.re (f x)) μ) (him : AEMeasurable (fun x => RCLike.im (f x)) μ) : AEMeasurable f μ := by convert! - AEMeasurable.add (M := 𝕜) (RCLike.measurable_ofReal.comp_aemeasurable hre) + AEMeasurable.fun_add (M := 𝕜) (RCLike.measurable_ofReal.comp_aemeasurable hre) ((RCLike.measurable_ofReal.comp_aemeasurable him).mul_const RCLike.I) exact (RCLike.re_add_im _).symm diff --git a/Mathlib/MeasureTheory/Function/SpecialFunctions/Sinc.lean b/Mathlib/MeasureTheory/Function/SpecialFunctions/Sinc.lean index dce1607860fa43..ad8628019735d1 100644 --- a/Mathlib/MeasureTheory/Function/SpecialFunctions/Sinc.lean +++ b/Mathlib/MeasureTheory/Function/SpecialFunctions/Sinc.lean @@ -6,7 +6,6 @@ Authors: Rémy Degenne module public import Mathlib.Analysis.SpecialFunctions.Trigonometric.Sinc -public import Mathlib.MeasureTheory.Function.SpecialFunctions.Basic public import Mathlib.MeasureTheory.Function.L1Space.Integrable /-! diff --git a/Mathlib/MeasureTheory/Function/StronglyMeasurable/AEStronglyMeasurable.lean b/Mathlib/MeasureTheory/Function/StronglyMeasurable/AEStronglyMeasurable.lean index ca9765aa83f906..2159a038b2d446 100644 --- a/Mathlib/MeasureTheory/Function/StronglyMeasurable/AEStronglyMeasurable.lean +++ b/Mathlib/MeasureTheory/Function/StronglyMeasurable/AEStronglyMeasurable.lean @@ -296,7 +296,7 @@ lemma of_measurableSpace_le_on {m' m₀ : MeasurableSpace α} {μ : Measure[m₀ section Arithmetic -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] protected theorem mul [Mul β] [ContinuousMul β] (hf : AEStronglyMeasurable[m] f μ) (hg : AEStronglyMeasurable[m] g μ) : AEStronglyMeasurable[m] (f * g) μ := ⟨hf.mk f * hg.mk g, by fun_prop, hf.ae_eq_mk.mul hg.ae_eq_mk⟩ @@ -311,17 +311,17 @@ protected theorem const_mul [Mul β] [ContinuousMul β] (hf : AEStronglyMeasurab AEStronglyMeasurable[m] (fun x => c * f x) μ := aestronglyMeasurable_const.mul hf -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] protected theorem inv [Inv β] [ContinuousInv β] (hf : AEStronglyMeasurable[m] f μ) : AEStronglyMeasurable[m] f⁻¹ μ := ⟨(hf.mk f)⁻¹, hf.stronglyMeasurable_mk.inv, hf.ae_eq_mk.inv⟩ -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem inv₀ [GroupWithZero β] [ContinuousInv₀ β] [MetrizableSpace β] (hf : AEStronglyMeasurable[m] f μ) : AEStronglyMeasurable[m] f⁻¹ μ := ⟨(hf.mk f)⁻¹, hf.stronglyMeasurable_mk.inv₀, hf.ae_eq_mk.inv⟩ -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] protected theorem div [Group β] [IsTopologicalGroup β] (hf : AEStronglyMeasurable[m] f μ) (hg : AEStronglyMeasurable[m] g μ) : AEStronglyMeasurable[m] (f / g) μ := ⟨hf.mk f / hg.mk g, hf.stronglyMeasurable_mk.div' hg.stronglyMeasurable_mk, @@ -345,26 +345,27 @@ theorem mul_iff_left [CommGroup β] [IsTopologicalGroup β] (hf : AEStronglyMeas AEStronglyMeasurable[m] (g * f) μ ↔ AEStronglyMeasurable[m] g μ := mul_comm g f ▸ AEStronglyMeasurable.mul_iff_right hf -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] protected theorem smul {𝕜} [TopologicalSpace 𝕜] [SMul 𝕜 β] [ContinuousSMul 𝕜 β] {f : α → 𝕜} {g : α → β} (hf : AEStronglyMeasurable[m] f μ) (hg : AEStronglyMeasurable[m] g μ) : - AEStronglyMeasurable[m] (fun x => f x • g x) μ := + AEStronglyMeasurable[m] (f • g) μ := continuous_smul.comp_aestronglyMeasurable (hf.prodMk hg) -@[to_additive (attr := fun_prop) const_nsmul] +@[to_additive (attr := to_fun (attr := fun_prop)) const_nsmul] protected theorem pow [Monoid β] [ContinuousMul β] (hf : AEStronglyMeasurable[m] f μ) (n : ℕ) : AEStronglyMeasurable[m] (f ^ n) μ := ⟨hf.mk f ^ n, hf.stronglyMeasurable_mk.pow _, hf.ae_eq_mk.pow_const _⟩ -@[to_additive (attr := fun_prop)] +@[to_additive (attr := to_fun (attr := fun_prop))] protected theorem const_smul {𝕜} [SMul 𝕜 β] [ContinuousConstSMul 𝕜 β] (hf : AEStronglyMeasurable[m] f μ) (c : 𝕜) : AEStronglyMeasurable[m] (c • f) μ := ⟨c • hf.mk f, hf.stronglyMeasurable_mk.const_smul c, hf.ae_eq_mk.const_smul c⟩ -@[to_additive (attr := fun_prop)] -protected theorem const_smul' {𝕜} [SMul 𝕜 β] [ContinuousConstSMul 𝕜 β] - (hf : AEStronglyMeasurable[m] f μ) (c : 𝕜) : AEStronglyMeasurable[m] (fun x => c • f x) μ := - hf.const_smul c +@[deprecated (since := "2026-06-26")] +alias const_smul' := AEStronglyMeasurable.fun_const_smul + +@[deprecated (since := "2026-06-26")] +alias const_vadd' := AEStronglyMeasurable.fun_const_vadd @[to_additive (attr := fun_prop)] protected theorem smul_const {𝕜} [TopologicalSpace 𝕜] [SMul 𝕜 β] [ContinuousSMul 𝕜 β] {f : α → 𝕜} @@ -384,13 +385,13 @@ end Star section Order -@[fun_prop] +@[to_fun (attr := fun_prop)] protected theorem sup [SemilatticeSup β] [ContinuousSup β] (hf : AEStronglyMeasurable f μ) (hg : AEStronglyMeasurable g μ) : AEStronglyMeasurable (f ⊔ g) μ := ⟨hf.mk f ⊔ hg.mk g, hf.stronglyMeasurable_mk.sup hg.stronglyMeasurable_mk, hf.ae_eq_mk.sup hg.ae_eq_mk⟩ -@[fun_prop] +@[to_fun (attr := fun_prop)] protected theorem inf [SemilatticeInf β] [ContinuousInf β] (hf : AEStronglyMeasurable f μ) (hg : AEStronglyMeasurable g μ) : AEStronglyMeasurable (f ⊓ g) μ := ⟨hf.mk f ⊓ hg.mk g, hf.stronglyMeasurable_mk.inf hg.stronglyMeasurable_mk, @@ -836,7 +837,7 @@ variable [GroupWithZero G₀] [MulAction G₀ β] [ContinuousConstSMul G₀ β] theorem _root_.aestronglyMeasurable_const_smul_iff (c : G) : AEStronglyMeasurable (fun x => c • f x) μ ↔ AEStronglyMeasurable f μ := - ⟨fun h => by simpa only [inv_smul_smul] using h.const_smul' c⁻¹, fun h => h.const_smul c⟩ + ⟨fun h => by simpa only [inv_smul_smul] using h.fun_const_smul c⁻¹, fun h => h.const_smul c⟩ nonrec theorem _root_.IsUnit.aestronglyMeasurable_const_smul_iff {c : M} (hc : IsUnit c) : AEStronglyMeasurable (fun x => c • f x) μ ↔ AEStronglyMeasurable f μ := diff --git a/Mathlib/MeasureTheory/Function/StronglyMeasurable/Basic.lean b/Mathlib/MeasureTheory/Function/StronglyMeasurable/Basic.lean index 8f3b79ed9a1d30..6f81c159bb59af 100644 --- a/Mathlib/MeasureTheory/Function/StronglyMeasurable/Basic.lean +++ b/Mathlib/MeasureTheory/Function/StronglyMeasurable/Basic.lean @@ -391,7 +391,7 @@ section Arithmetic variable {mα : MeasurableSpace α} [TopologicalSpace β] -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] protected theorem mul [Mul β] [ContinuousMul β] (hf : StronglyMeasurable f) (hg : StronglyMeasurable g) : StronglyMeasurable (f * g) := ⟨fun n => hf.approx n * hg.approx n, fun x => (hf.tendsto_approx x).mul (hg.tendsto_approx x)⟩ @@ -406,17 +406,17 @@ theorem const_mul [Mul β] [ContinuousMul β] (hf : StronglyMeasurable f) (c : StronglyMeasurable fun x => c * f x := stronglyMeasurable_const.mul hf -@[to_additive (attr := fun_prop) const_nsmul] +@[to_additive (attr := to_fun (attr := fun_prop)) const_nsmul] protected theorem pow [Monoid β] [ContinuousMul β] (hf : StronglyMeasurable f) (n : ℕ) : StronglyMeasurable (f ^ n) := ⟨fun k => hf.approx k ^ n, fun x => (hf.tendsto_approx x).pow n⟩ -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] protected theorem inv [Inv β] [ContinuousInv β] (hf : StronglyMeasurable f) : StronglyMeasurable f⁻¹ := ⟨fun n => (hf.approx n)⁻¹, fun x => (hf.tendsto_approx x).inv⟩ -@[fun_prop] +@[to_fun (attr := fun_prop)] protected theorem inv₀ [GroupWithZero β] [ContinuousInv₀ β] [MetrizableSpace β] (hf : StronglyMeasurable f) : StronglyMeasurable f⁻¹ := by borelize β @@ -431,7 +431,7 @@ protected theorem inv₀ [GroupWithZero β] [ContinuousInv₀ β] [MetrizableSpa Pi.inv_apply, mem_setOf_eq, not_false_eq_true, indicator_of_mem] apply (hf.tendsto_approx x).inv₀ h -@[to_additive (attr := fun_prop) sub] +@[to_additive (attr := to_fun (attr := fun_prop)) sub] protected theorem div' [Div β] [ContinuousDiv β] (hf : StronglyMeasurable f) (hg : StronglyMeasurable g) : StronglyMeasurable (f / g) := ⟨fun n => hf.approx n / hg.approx n, fun x => (hf.tendsto_approx x).div' (hg.tendsto_approx x)⟩ @@ -468,21 +468,22 @@ theorem mul_iff_left [CommGroup β] [IsTopologicalGroup β] (hf : StronglyMeasur StronglyMeasurable (g * f) ↔ StronglyMeasurable g := mul_comm g f ▸ mul_iff_right hf -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] protected theorem smul {𝕜} [TopologicalSpace 𝕜] [SMul 𝕜 β] [ContinuousSMul 𝕜 β] {f : α → 𝕜} {g : α → β} (hf : StronglyMeasurable f) (hg : StronglyMeasurable g) : - StronglyMeasurable fun x => f x • g x := + StronglyMeasurable (f • g) := continuous_smul.comp_stronglyMeasurable (hf.prodMk hg) -@[to_additive (attr := fun_prop)] +@[to_additive (attr := to_fun (attr := fun_prop))] protected theorem const_smul {𝕜} [SMul 𝕜 β] [ContinuousConstSMul 𝕜 β] (hf : StronglyMeasurable f) (c : 𝕜) : StronglyMeasurable (c • f) := ⟨fun n => c • hf.approx n, fun x => (hf.tendsto_approx x).const_smul c⟩ -@[to_additive (attr := fun_prop)] -protected theorem const_smul' {𝕜} [SMul 𝕜 β] [ContinuousConstSMul 𝕜 β] (hf : StronglyMeasurable f) - (c : 𝕜) : StronglyMeasurable fun x => c • f x := - hf.const_smul c +@[deprecated (since := "2026-06-26")] +alias const_smul' := StronglyMeasurable.fun_const_smul + +@[deprecated (since := "2026-06-26")] +alias const_vadd' := StronglyMeasurable.fun_const_vadd @[to_additive (attr := fun_prop)] protected theorem smul_const {𝕜} [TopologicalSpace 𝕜] [SMul 𝕜 β] [ContinuousSMul 𝕜 β] {f : α → 𝕜} @@ -546,7 +547,7 @@ variable [GroupWithZero G₀] [MulAction G₀ β] [ContinuousConstSMul G₀ β] theorem _root_.stronglyMeasurable_const_smul_iff {m : MeasurableSpace α} (c : G) : (StronglyMeasurable fun x => c • f x) ↔ StronglyMeasurable f := - ⟨fun h => by simpa only [inv_smul_smul] using h.const_smul' c⁻¹, fun h => h.const_smul c⟩ + ⟨fun h => by simpa only [inv_smul_smul] using h.fun_const_smul c⁻¹, fun h => h.const_smul c⟩ nonrec theorem _root_.IsUnit.stronglyMeasurable_const_smul_iff {_ : MeasurableSpace α} {c : M} (hc : IsUnit c) : @@ -566,13 +567,13 @@ variable [MeasurableSpace α] [TopologicalSpace β] open Filter -@[fun_prop] +@[to_fun (attr := fun_prop)] protected theorem sup [Max β] [ContinuousSup β] (hf : StronglyMeasurable f) (hg : StronglyMeasurable g) : StronglyMeasurable (f ⊔ g) := ⟨fun n => hf.approx n ⊔ hg.approx n, fun x => (hf.tendsto_approx x).sup_nhds (hg.tendsto_approx x)⟩ -@[fun_prop] +@[to_fun (attr := fun_prop)] protected theorem inf [Min β] [ContinuousInf β] (hf : StronglyMeasurable f) (hg : StronglyMeasurable g) : StronglyMeasurable (f ⊓ g) := ⟨fun n => hf.approx n ⊓ hg.approx n, fun x => diff --git a/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lemmas.lean b/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lemmas.lean index 179b8d2e1e18f4..f861c5f6d0e688 100644 --- a/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lemmas.lean +++ b/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lemmas.lean @@ -6,7 +6,6 @@ Authors: Rémy Degenne, Sébastien Gouëzel module public import Mathlib.Analysis.Normed.Operator.BoundedLinearMaps -public import Mathlib.Dynamics.Ergodic.MeasurePreserving public import Mathlib.MeasureTheory.Function.StronglyMeasurable.AEStronglyMeasurable public import Mathlib.MeasureTheory.Measure.WithDensity public import Mathlib.Topology.Algebra.Module.FiniteDimension diff --git a/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lp.lean b/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lp.lean index fd29fbb9e2cff4..9655fb43723819 100644 --- a/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lp.lean +++ b/Mathlib/MeasureTheory/Function/StronglyMeasurable/Lp.lean @@ -6,7 +6,6 @@ Authors: Rémy Degenne module public import Mathlib.MeasureTheory.Function.SimpleFuncDenseLp -public import Mathlib.MeasureTheory.Function.StronglyMeasurable.Lemmas /-! # Finitely strongly measurable functions in `Lp` diff --git a/Mathlib/MeasureTheory/Function/UnifTight.lean b/Mathlib/MeasureTheory/Function/UnifTight.lean index da99d5069c6ba9..8ae43e2c33eb48 100644 --- a/Mathlib/MeasureTheory/Function/UnifTight.lean +++ b/Mathlib/MeasureTheory/Function/UnifTight.lean @@ -5,7 +5,6 @@ Authors: Igor Khavkine -/ module -public import Mathlib.MeasureTheory.Function.ConvergenceInMeasure public import Mathlib.MeasureTheory.Function.UniformIntegrable /-! diff --git a/Mathlib/MeasureTheory/Function/UniformIntegrable.lean b/Mathlib/MeasureTheory/Function/UniformIntegrable.lean index 8844cd4a0538c6..8d2058fb69dd68 100644 --- a/Mathlib/MeasureTheory/Function/UniformIntegrable.lean +++ b/Mathlib/MeasureTheory/Function/UniformIntegrable.lean @@ -5,7 +5,6 @@ Authors: Kexing Ying -/ module -public import Mathlib.MeasureTheory.Function.ConvergenceInMeasure public import Mathlib.MeasureTheory.Function.L1Space.Integrable /-! diff --git a/Mathlib/MeasureTheory/Group/AEStabilizer.lean b/Mathlib/MeasureTheory/Group/AEStabilizer.lean index 974575ccfaa36c..063144fc54b2bf 100644 --- a/Mathlib/MeasureTheory/Group/AEStabilizer.lean +++ b/Mathlib/MeasureTheory/Group/AEStabilizer.lean @@ -6,7 +6,6 @@ Authors: Yury Kudryashov module public import Mathlib.MeasureTheory.Group.Action -public import Mathlib.Order.Filter.EventuallyConst /-! # A.e. stabilizer of a set diff --git a/Mathlib/MeasureTheory/Group/Action.lean b/Mathlib/MeasureTheory/Group/Action.lean index 1c421500555465..eeb56066690caa 100644 --- a/Mathlib/MeasureTheory/Group/Action.lean +++ b/Mathlib/MeasureTheory/Group/Action.lean @@ -7,11 +7,8 @@ module public import Mathlib.Dynamics.Ergodic.MeasurePreserving public import Mathlib.Dynamics.Minimal -public import Mathlib.GroupTheory.GroupAction.Hom -public import Mathlib.MeasureTheory.Group.MeasurableEquiv public import Mathlib.MeasureTheory.Measure.Regular public import Mathlib.MeasureTheory.Group.Defs -public import Mathlib.Order.Filter.EventuallyConst /-! # Measures invariant under group actions diff --git a/Mathlib/MeasureTheory/Group/AddCircle.lean b/Mathlib/MeasureTheory/Group/AddCircle.lean index c6349dc3a5d5ec..1274c8372799de 100644 --- a/Mathlib/MeasureTheory/Group/AddCircle.lean +++ b/Mathlib/MeasureTheory/Group/AddCircle.lean @@ -6,7 +6,6 @@ Authors: Oliver Nash module public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Periodic -public import Mathlib.Data.ZMod.QuotientGroup public import Mathlib.MeasureTheory.Group.AEStabilizer /-! diff --git a/Mathlib/MeasureTheory/Group/Arithmetic.lean b/Mathlib/MeasureTheory/Group/Arithmetic.lean index d5e72ff2db5073..f4f7730d6a9db9 100644 --- a/Mathlib/MeasureTheory/Group/Arithmetic.lean +++ b/Mathlib/MeasureTheory/Group/Arithmetic.lean @@ -114,9 +114,9 @@ theorem AEMeasurable.mul_const [MeasurableMul M] (hf : AEMeasurable f μ) (c : M AEMeasurable (fun x => f x * c) μ := (measurable_mul_const c).comp_aemeasurable hf -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem Measurable.mul [MeasurableMul₂ M] (hf : Measurable f) (hg : Measurable g) : - Measurable fun a => f a * g a := + Measurable (f * g) := measurable_mul.comp (hf.prodMk hg) /-- Compositional version of `Measurable.mul` for use by `fun_prop`. -/ @@ -126,15 +126,13 @@ lemma Measurable.mul' [MeasurableMul₂ M] {f g : α → β → M} {h : α → (hg : Measurable ↿g) (hh : Measurable h) : Measurable fun a ↦ (f a * g a) (h a) := by dsimp; fun_prop -@[to_additive (attr := fun_prop)] -theorem AEMeasurable.mul' [MeasurableMul₂ M] (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : +@[to_fun (attr := to_additive (attr := fun_prop))] +theorem AEMeasurable.mul [MeasurableMul₂ M] (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : AEMeasurable (f * g) μ := measurable_mul.comp_aemeasurable (hf.prodMk hg) -@[to_additive (attr := fun_prop)] -theorem AEMeasurable.mul [MeasurableMul₂ M] (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : - AEMeasurable (fun a => f a * g a) μ := - measurable_mul.comp_aemeasurable (hf.prodMk hg) +@[deprecated (since := "2026-06-26")] alias AEMeasurable.mul' := AEMeasurable.mul +@[deprecated (since := "2026-06-26")] alias AEMeasurable.add' := AEMeasurable.add @[to_additive] instance (priority := 100) MeasurableMul₂.toMeasurableMul [MeasurableMul₂ M] : @@ -267,9 +265,9 @@ theorem AEMeasurable.div_const [MeasurableDiv G] (hf : AEMeasurable f μ) (c : G AEMeasurable (fun x => f x / c) μ := (MeasurableDiv.measurable_div_const c).comp_aemeasurable hf -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem Measurable.div [MeasurableDiv₂ G] (hf : Measurable f) (hg : Measurable g) : - Measurable fun a => f a / g a := + Measurable (f / g) := measurable_div.comp (hf.prodMk hg) @[to_additive (attr := fun_prop)] @@ -277,15 +275,13 @@ lemma Measurable.div' [MeasurableDiv₂ G] {f g : α → β → G} {h : α → (hg : Measurable ↿g) (hh : Measurable h) : Measurable fun a ↦ (f a / g a) (h a) := by dsimp; fun_prop -@[to_additive (attr := fun_prop)] -theorem AEMeasurable.div' [MeasurableDiv₂ G] (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : +@[to_fun (attr := to_additive (attr := fun_prop))] +theorem AEMeasurable.div [MeasurableDiv₂ G] (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : AEMeasurable (f / g) μ := measurable_div.comp_aemeasurable (hf.prodMk hg) -@[to_additive (attr := fun_prop)] -theorem AEMeasurable.div [MeasurableDiv₂ G] (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : - AEMeasurable (fun a => f a / g a) μ := - measurable_div.comp_aemeasurable (hf.prodMk hg) +@[deprecated (since := "2026-06-26")] alias AEMeasurable.div' := AEMeasurable.div +@[deprecated (since := "2026-06-26")] alias AEMeasurable.sub' := AEMeasurable.sub @[to_additive] instance (priority := 100) MeasurableDiv₂.toMeasurableDiv [MeasurableDiv₂ G] : @@ -349,23 +345,23 @@ section Inv variable {G α : Type*} [Inv G] [MeasurableSpace G] [MeasurableInv G] {m : MeasurableSpace α} {f : α → G} {μ : Measure α} -@[to_additive (attr := fun_prop)] -theorem Measurable.inv (hf : Measurable f) : Measurable fun x => (f x)⁻¹ := +@[to_fun (attr := to_additive (attr := fun_prop))] +theorem Measurable.inv (hf : Measurable f) : Measurable f⁻¹ := measurable_inv.comp hf -@[to_additive (attr := fun_prop)] -theorem AEMeasurable.inv (hf : AEMeasurable f μ) : AEMeasurable (fun x => (f x)⁻¹) μ := +@[to_fun (attr := to_additive (attr := fun_prop))] +theorem AEMeasurable.inv (hf : AEMeasurable f μ) : AEMeasurable f⁻¹ μ := measurable_inv.comp_aemeasurable hf @[to_additive (attr := simp)] theorem measurable_inv_iff {G : Type*} [InvolutiveInv G] [MeasurableSpace G] [MeasurableInv G] {f : α → G} : (Measurable fun x => (f x)⁻¹) ↔ Measurable f := - ⟨fun h => by simpa only [inv_inv] using h.inv, fun h => h.inv⟩ + ⟨fun h => by simpa only [inv_inv] using h.fun_inv, fun h => h.inv⟩ @[to_additive (attr := simp)] theorem aemeasurable_inv_iff {G : Type*} [InvolutiveInv G] [MeasurableSpace G] [MeasurableInv G] {f : α → G} : AEMeasurable (fun x => (f x)⁻¹) μ ↔ AEMeasurable f μ := - ⟨fun h => by simpa only [inv_inv] using h.inv, fun h => h.inv⟩ + ⟨fun h => by simpa only [inv_inv] using h.fun_inv, fun h => h.inv⟩ @[to_additive] instance Pi.measurableInv {ι : Type*} {α : ι → Type*} [∀ i, Inv (α i)] @@ -535,9 +531,9 @@ end MeasurableConstSMul variable [MeasurableSpace M] -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem Measurable.smul [MeasurableSMul₂ M X] (hf : Measurable f) (hg : Measurable g) : - Measurable fun x => f x • g x := + Measurable (f • g) := measurable_smul.comp (hf.prodMk hg) /-- Compositional version of `Measurable.smul` for use by `fun_prop`. -/ @@ -547,9 +543,9 @@ lemma Measurable.smul' [MeasurableSMul₂ M X] {f : α → β → M} {g : α → (hf : Measurable ↿f) (hg : Measurable ↿g) (hh : Measurable h) : Measurable fun a ↦ (f a • g a) (h a) := by dsimp; fun_prop -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem AEMeasurable.smul [MeasurableSMul₂ M X] {μ : Measure α} (hf : AEMeasurable f μ) - (hg : AEMeasurable g μ) : AEMeasurable (fun x => f x • g x) μ := + (hg : AEMeasurable g μ) : AEMeasurable (f • g) μ := MeasurableSMul₂.measurable_smul.comp_aemeasurable (hf.prodMk hg) @[to_additive] diff --git a/Mathlib/MeasureTheory/Group/FoelnerFilter.lean b/Mathlib/MeasureTheory/Group/FoelnerFilter.lean index 054aa9a3ad1d93..2586b9b844e4c1 100644 --- a/Mathlib/MeasureTheory/Group/FoelnerFilter.lean +++ b/Mathlib/MeasureTheory/Group/FoelnerFilter.lean @@ -5,9 +5,7 @@ Authors: Yaël Dillies, Stefano Rocca -/ module -public import Mathlib.MeasureTheory.Group.Defs public import Mathlib.MeasureTheory.Group.Action -public import Mathlib.MeasureTheory.Measure.Typeclasses.Finite /-! # Følner sequences and filters - definitions and properties diff --git a/Mathlib/MeasureTheory/Group/FundamentalDomain.lean b/Mathlib/MeasureTheory/Group/FundamentalDomain.lean index b1f075c9200048..237ae6b34da3f9 100644 --- a/Mathlib/MeasureTheory/Group/FundamentalDomain.lean +++ b/Mathlib/MeasureTheory/Group/FundamentalDomain.lean @@ -5,9 +5,6 @@ Authors: Yury Kudryashov, Alex Kontorovich, Heather Macbeth -/ module -public import Mathlib.MeasureTheory.Group.Action -public import Mathlib.MeasureTheory.Group.Pointwise -public import Mathlib.MeasureTheory.Integral.Lebesgue.Map public import Mathlib.MeasureTheory.Integral.Bochner.Set /-! diff --git a/Mathlib/MeasureTheory/Group/Integral.lean b/Mathlib/MeasureTheory/Group/Integral.lean index df6e5ee5a0de4f..2b0913a40a5b65 100644 --- a/Mathlib/MeasureTheory/Group/Integral.lean +++ b/Mathlib/MeasureTheory/Group/Integral.lean @@ -6,6 +6,7 @@ Authors: Floris van Doorn module public import Mathlib.MeasureTheory.Integral.Bochner.Basic +public import Mathlib.MeasureTheory.Integral.IntegrableOn public import Mathlib.MeasureTheory.Group.Measure /-! diff --git a/Mathlib/MeasureTheory/Group/IntegralConvolution.lean b/Mathlib/MeasureTheory/Group/IntegralConvolution.lean index a2c0da03a44d25..3693c5c4ca3b52 100644 --- a/Mathlib/MeasureTheory/Group/IntegralConvolution.lean +++ b/Mathlib/MeasureTheory/Group/IntegralConvolution.lean @@ -5,7 +5,6 @@ Authors: Rémy Degenne -/ module -public import Mathlib.MeasureTheory.Group.Convolution public import Mathlib.MeasureTheory.Integral.Prod /-! diff --git a/Mathlib/MeasureTheory/Group/Measure.lean b/Mathlib/MeasureTheory/Group/Measure.lean index e38b897cfb8d77..8686ad446ae983 100644 --- a/Mathlib/MeasureTheory/Group/Measure.lean +++ b/Mathlib/MeasureTheory/Group/Measure.lean @@ -12,7 +12,6 @@ public import Mathlib.MeasureTheory.Group.Pointwise public import Mathlib.MeasureTheory.Measure.Prod public import Mathlib.Topology.Algebra.Module.Equiv public import Mathlib.Topology.ContinuousMap.CocompactMap -public import Mathlib.Topology.Algebra.ContinuousMonoidHom /-! # Measures on Groups @@ -918,20 +917,20 @@ instance prod.instIsHaarMeasure {G : Type*} [Group G] [TopologicalSpace G] {_ : (ν : Measure H) [IsHaarMeasure μ] [IsHaarMeasure ν] [SFinite μ] [SFinite ν] [MeasurableMul G] [MeasurableMul H] : IsHaarMeasure (μ.prod ν) where -/-- If the neutral element of a group is not isolated, then a Haar measure on this group has -no atoms. +/-- If the neutral element of a group is not isolated, then a Haar measure on this group has value +zero on singletons. The additive version of this instance applies in particular to show that an additive Haar measure on a nontrivial finite-dimensional real vector space has no atom. -/ @[to_additive /-- If the zero element of an additive group is not isolated, then an additive Haar measure on this -group has no atoms. +group has value zero on singletons. This applies in particular to show that an additive Haar measure on a nontrivial finite-dimensional real vector space has no atom. -/] -instance (priority := 100) IsHaarMeasure.noAtoms [IsTopologicalGroup G] [BorelSpace G] [T1Space G] - [WeaklyLocallyCompactSpace G] [(𝓝[≠] (1 : G)).NeBot] (μ : Measure G) [μ.IsHaarMeasure] : - NoAtoms μ := by +instance (priority := 100) IsHaarMeasure.nullSingletonClass [IsTopologicalGroup G] [BorelSpace G] + [T1Space G] [WeaklyLocallyCompactSpace G] [(𝓝[≠] (1 : G)).NeBot] (μ : Measure G) + [μ.IsHaarMeasure] : NullSingletonClass μ := by cases eq_or_ne (μ 1) 0 with | inl h => constructor; simpa | inr h => @@ -940,6 +939,9 @@ instance (priority := 100) IsHaarMeasure.noAtoms [IsTopologicalGroup G] [BorelSp exact absurd (K_inf.meas_eq_top ⟨_, h, fun x _ ↦ (haar_singleton _ _).ge⟩) K_compact.measure_lt_top.ne +@[deprecated (since := "2026-06-09")] +alias IsHaarMeasure.noAtoms := IsHaarMeasure.nullSingletonClass + instance IsAddHaarMeasure.domSMul {G A : Type*} [Group G] [AddCommGroup A] [DistribMulAction G A] [MeasurableSpace A] [TopologicalSpace A] [BorelSpace A] [IsTopologicalAddGroup A] [ContinuousConstSMul G A] {μ : Measure A} [μ.IsAddHaarMeasure] (g : Gᵈᵐᵃ) : diff --git a/Mathlib/MeasureTheory/Group/ModularCharacter.lean b/Mathlib/MeasureTheory/Group/ModularCharacter.lean index f3b7f584eb3649..9ac2976d854fc5 100644 --- a/Mathlib/MeasureTheory/Group/ModularCharacter.lean +++ b/Mathlib/MeasureTheory/Group/ModularCharacter.lean @@ -5,12 +5,7 @@ Authors: Noam Atar -/ module -public import Mathlib.MeasureTheory.Function.LocallyIntegrable -public import Mathlib.MeasureTheory.Group.Integral -public import Mathlib.MeasureTheory.Group.Measure -public import Mathlib.Topology.Metrizable.Urysohn public import Mathlib.MeasureTheory.Measure.Haar.Unique -public import Mathlib.MeasureTheory.Constructions.BorelSpace.Basic /-! # Modular character of a locally compact group diff --git a/Mathlib/MeasureTheory/Group/Prod.lean b/Mathlib/MeasureTheory/Group/Prod.lean index 50a52b5efd9f88..f5676f16cc5dd7 100644 --- a/Mathlib/MeasureTheory/Group/Prod.lean +++ b/Mathlib/MeasureTheory/Group/Prod.lean @@ -6,10 +6,10 @@ Authors: Floris van Doorn module public import Mathlib.MeasureTheory.Group.Measure -public import Mathlib.MeasureTheory.Measure.Prod /-! # Measure theory in the product of groups + In this file we show properties about measure theory in products of measurable groups and properties of iterated integrals in measurable groups. diff --git a/Mathlib/MeasureTheory/Integral/Asymptotics.lean b/Mathlib/MeasureTheory/Integral/Asymptotics.lean index bc9f1f337f9e9f..e942352e0cca11 100644 --- a/Mathlib/MeasureTheory/Integral/Asymptotics.lean +++ b/Mathlib/MeasureTheory/Integral/Asymptotics.lean @@ -5,7 +5,6 @@ Authors: Lawrence Wu -/ module -public import Mathlib.MeasureTheory.Group.Measure public import Mathlib.MeasureTheory.Integral.Bochner.ContinuousLinearMap /-! diff --git a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean index 577389d0ba23ef..fab92ef18f6c86 100644 --- a/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean +++ b/Mathlib/MeasureTheory/Integral/Bochner/Basic.lean @@ -5,11 +5,7 @@ Authors: Zhouhang Zhou, Yury Kudryashov, Sébastien Gouëzel, Rémy Degenne -/ module -public import Mathlib.MeasureTheory.Group.MeasurableEquiv public import Mathlib.MeasureTheory.Integral.Bochner.L1 -public import Mathlib.MeasureTheory.Integral.IntegrableOn -public import Mathlib.MeasureTheory.Measure.OpenPos -public import Mathlib.MeasureTheory.Measure.Real /-! # Bochner integral @@ -1367,8 +1363,8 @@ attribute [local instance] monadLiftOptionMetaM in This extension only proves non-negativity, strict positivity is more delicate for integration and requires more assumptions. -/ @[positivity MeasureTheory.integral _ _] -meta def evalIntegral : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalIntegral : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match u, α, e with | 0, ~q(ℝ), ~q(@MeasureTheory.integral $i ℝ _ $inst2 _ _ $f) => let i : Q($i) ← mkFreshExprMVarQ q($i) .syntheticOpaque diff --git a/Mathlib/MeasureTheory/Integral/Bochner/Set.lean b/Mathlib/MeasureTheory/Integral/Bochner/Set.lean index 8d5f7a068e9b7f..b1dce490b5b59a 100644 --- a/Mathlib/MeasureTheory/Integral/Bochner/Set.lean +++ b/Mathlib/MeasureTheory/Integral/Bochner/Set.lean @@ -666,7 +666,7 @@ theorem setIntegral_trim {X} {m m0 : MeasurableSpace X} {μ : Measure X} (hm : m /-! ### Lemmas about adding and removing interval boundaries The primed lemmas take explicit arguments about the endpoint having zero measure, while the -unprimed ones use `[NoAtoms μ]`. +unprimed ones use `[NullSingletonClass μ]`. -/ section PartialOrder @@ -701,7 +701,7 @@ theorem integral_Ici_eq_integral_Ioi' (hx : μ {x} = 0) : ∫ t in Ici x, f t ∂μ = ∫ t in Ioi x, f t ∂μ := setIntegral_congr_set (Ioi_ae_eq_Ici' hx).symm -variable [NoAtoms μ] +variable [NullSingletonClass μ] theorem integral_Icc_eq_integral_Ioc : ∫ t in Icc x y, f t ∂μ = ∫ t in Ioc x y, f t ∂μ := integral_Icc_eq_integral_Ioc' <| measure_singleton x @@ -885,6 +885,23 @@ lemma integral_le_measure {f : X → ℝ} {s : Set X} · intro x hx simpa [g] using h's x hx +lemma setIntegral_mono_of_nonneg {g : X → ℝ} (hf : ∀ x ∈ s, 0 ≤ f x) + (h : ∀ x ∈ s, f x ≤ g x) (hg : IntegrableOn g s μ) : + ∫ x in s, f x ∂μ ≤ ∫ x in s, g x ∂μ := by + by_cases h'f : AEStronglyMeasurable f (μ.restrict s); swap + · rw [integral_non_aestronglyMeasurable h'f] + apply integral_nonneg_of_ae + apply (ae_restrict_iff₀ ?_).2 + · filter_upwards with x hx using (hf x hx).trans (h x hx) + · exact nullMeasurableSet_le aemeasurable_const hg.aemeasurable + refine integral_mono_of_nonneg ?_ hg ?_ + · apply (ae_restrict_iff₀ ?_).2 + · filter_upwards with x hx using hf x hx + · exact nullMeasurableSet_le aemeasurable_const h'f.aemeasurable + · apply (ae_restrict_iff₀ ?_).2 + · filter_upwards with x hx using h x hx + · exact nullMeasurableSet_le h'f.aemeasurable hg.aemeasurable + end Nonneg section IntegrableUnion diff --git a/Mathlib/MeasureTheory/Integral/Bochner/SumMeasure.lean b/Mathlib/MeasureTheory/Integral/Bochner/SumMeasure.lean index 3eebbeb46aa873..a34b52e13c7423 100644 --- a/Mathlib/MeasureTheory/Integral/Bochner/SumMeasure.lean +++ b/Mathlib/MeasureTheory/Integral/Bochner/SumMeasure.lean @@ -6,6 +6,7 @@ Authors: Etienne Marion module public import Mathlib.MeasureTheory.Integral.Bochner.Basic +public import Mathlib.MeasureTheory.Integral.IntegrableOn import Mathlib.Analysis.Normed.Module.FiniteDimension diff --git a/Mathlib/MeasureTheory/Integral/Bochner/VitaliCaratheodory.lean b/Mathlib/MeasureTheory/Integral/Bochner/VitaliCaratheodory.lean index 1c83d9a1c4c386..58a130612dcecb 100644 --- a/Mathlib/MeasureTheory/Integral/Bochner/VitaliCaratheodory.lean +++ b/Mathlib/MeasureTheory/Integral/Bochner/VitaliCaratheodory.lean @@ -5,10 +5,7 @@ Authors: Sébastien Gouëzel -/ module -public import Mathlib.MeasureTheory.Measure.Regular -public import Mathlib.Topology.Semicontinuity.Basic public import Mathlib.MeasureTheory.Integral.Bochner.Basic -public import Mathlib.Topology.Instances.EReal.Lemmas /-! # Vitali-Carathéodory theorem diff --git a/Mathlib/MeasureTheory/Integral/CircleAverage.lean b/Mathlib/MeasureTheory/Integral/CircleAverage.lean index 73b84211699661..11d99983cbf002 100644 --- a/Mathlib/MeasureTheory/Integral/CircleAverage.lean +++ b/Mathlib/MeasureTheory/Integral/CircleAverage.lean @@ -7,7 +7,6 @@ module public import Mathlib.MeasureTheory.Integral.CircleIntegral public import Mathlib.MeasureTheory.Integral.IntervalAverage -public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Periodic /-! # Circle Averages diff --git a/Mathlib/MeasureTheory/Integral/CircleIntegral.lean b/Mathlib/MeasureTheory/Integral/CircleIntegral.lean index db48abb1c5e777..be51d85b30cd1a 100644 --- a/Mathlib/MeasureTheory/Integral/CircleIntegral.lean +++ b/Mathlib/MeasureTheory/Integral/CircleIntegral.lean @@ -327,7 +327,7 @@ theorem circleIntegrable_iff [NormedSpace ℂ E] {f : ℂ → E} {c : ℂ} (R : · have H : ∀ {θ}, circleMap 0 R θ * I ≠ 0 := fun {θ} => by simp [h₀, I_ne_zero] simpa only [inv_smul_smul₀ H] using ((continuous_circleMap 0 R).aestronglyMeasurable.mul_const - I).aemeasurable.inv.aestronglyMeasurable.smul h.aestronglyMeasurable + I).aemeasurable.fun_inv.aestronglyMeasurable.fun_smul h.aestronglyMeasurable · simp [norm_smul, h₀] theorem ContinuousOn.circleIntegrable' {f : ℂ → E} {c : ℂ} {R : ℝ} @@ -413,8 +413,7 @@ theorem _root_.TendstoUniformlyOn.tendsto_circleIntegral_of_continuousOn intro ε hε rcases exists_pos_mul_lt hε R with ⟨δ, hδ₀, hRδ⟩ refine (h δ hδ₀).mono fun i hi x hx ↦ ?_ - grw [hi (circleMap c R x) (by simp [hR])] - exact hRδ + grw [← hRδ, hi (circleMap c R x) (by simp [hR])] namespace circleIntegral diff --git a/Mathlib/MeasureTheory/Integral/CircleTransform.lean b/Mathlib/MeasureTheory/Integral/CircleTransform.lean index b26e92516f63da..72e288c0158436 100644 --- a/Mathlib/MeasureTheory/Integral/CircleTransform.lean +++ b/Mathlib/MeasureTheory/Integral/CircleTransform.lean @@ -5,7 +5,6 @@ Authors: Chris Birkbeck -/ module -public import Mathlib.Data.Complex.Basic public import Mathlib.MeasureTheory.Integral.CircleIntegral /-! @@ -97,7 +96,7 @@ theorem continuousOn_prod_circle_transform_function {R r : ℝ} (hr : r < R) {z theorem continuousOn_norm_circleTransformBoundingFunction {R r : ℝ} (hr : r < R) (z : ℂ) : ContinuousOn ((‖·‖) ∘ circleTransformBoundingFunction R z) (closedBall z r ×ˢ univ) := by have : ContinuousOn (circleTransformBoundingFunction R z) (closedBall z r ×ˢ univ) := by - apply_rules [ContinuousOn.smul, continuousOn_const] + apply_rules [ContinuousOn.fun_smul, continuousOn_const] · simp only [deriv_circleMap] apply_rules [ContinuousOn.mul, (continuous_circleMap 0 R).comp_continuousOn continuousOn_snd, continuousOn_const] diff --git a/Mathlib/MeasureTheory/Integral/CurveIntegral/Basic.lean b/Mathlib/MeasureTheory/Integral/CurveIntegral/Basic.lean index 4d7d68eff2b176..dd62fc98ff3080 100644 --- a/Mathlib/MeasureTheory/Integral/CurveIntegral/Basic.lean +++ b/Mathlib/MeasureTheory/Integral/CurveIntegral/Basic.lean @@ -9,7 +9,6 @@ public import Mathlib.Algebra.Order.Field.Pointwise public import Mathlib.Analysis.Calculus.ContDiff.Deriv public import Mathlib.Analysis.Calculus.Deriv.AffineMap public import Mathlib.Analysis.Calculus.Deriv.Shift -public import Mathlib.Analysis.Normed.Module.Convex public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic /-! diff --git a/Mathlib/MeasureTheory/Integral/DivergenceTheorem.lean b/Mathlib/MeasureTheory/Integral/DivergenceTheorem.lean index e921a2785fab91..018872f8d0a00e 100644 --- a/Mathlib/MeasureTheory/Integral/DivergenceTheorem.lean +++ b/Mathlib/MeasureTheory/Integral/DivergenceTheorem.lean @@ -10,7 +10,6 @@ public import Mathlib.Analysis.BoxIntegral.Integrability public import Mathlib.Analysis.Calculus.Deriv.Basic public import Mathlib.Analysis.Calculus.FDeriv.Equiv public import Mathlib.MeasureTheory.Integral.Prod -public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic /-! # Divergence theorem for Bochner integral diff --git a/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean b/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean index 76231ac56f25e2..9ddda1d5559bff 100644 --- a/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean +++ b/Mathlib/MeasureTheory/Integral/DominatedConvergence.lean @@ -5,7 +5,6 @@ Authors: Zhouhang Zhou, Yury Kudryashov, Patrick Massot, Louis (Yiyang) Liu -/ module -public import Mathlib.MeasureTheory.Constructions.Polish.StronglyMeasurable public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic import Mathlib.Topology.Algebra.IsUniformGroup.Order @@ -435,7 +434,7 @@ theorem continuousAt_parametric_primitive_of_dominated [FirstCountableTopology X rw [nhds_prod_eq] exact (continuous_abs.tendsto' _ _ abs_zero).comp (this.comp tendsto_snd) -variable [NoAtoms μ] +variable [NullSingletonClass μ] theorem continuousOn_primitive (h_int : IntegrableOn f (Icc a b) μ) : ContinuousOn (fun x => ∫ t in Ioc a x, f t ∂μ) (Icc a b) := by @@ -636,8 +635,8 @@ theorem continuousWithinAt_Ici_primitive_Ioi {a₀ : ℝ} (hf : IntegrableOn f ( · filter_upwards [mem_nhdsWithin_of_mem_nhds (Iio_mem_nhds hx)] with a ha using by grind · filter_upwards [self_mem_nhdsWithin] with a ha using by grind -theorem continuousOn_Ici_primitive_Ioi [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Ioi a₀) μ) : - ContinuousOn (fun b ↦ ∫ x in Ioi b, f x ∂μ) (Ici a₀) := by +theorem continuousOn_Ici_primitive_Ioi [NullSingletonClass μ] {a₀ : ℝ} + (hf : IntegrableOn f (Ioi a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Ioi b, f x ∂μ) (Ici a₀) := by intro a (ha : a₀ ≤ a) rw [continuousWithinAt_iff_continuous_left_right] constructor @@ -671,8 +670,8 @@ theorem continuousWithinAt_Iic_primitive_Iio {a₀ : ℝ} (hf : IntegrableOn f ( · filter_upwards [mem_nhdsWithin_of_mem_nhds (Ioi_mem_nhds hx)] with a ha using by grind · filter_upwards [self_mem_nhdsWithin] with a ha using by grind -theorem continuousOn_Iic_primitive_Iio [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Iio a₀) μ) : - ContinuousOn (fun b ↦ ∫ x in Iio b, f x ∂μ) (Iic a₀) := by +theorem continuousOn_Iic_primitive_Iio [NullSingletonClass μ] {a₀ : ℝ} + (hf : IntegrableOn f (Iio a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Iio b, f x ∂μ) (Iic a₀) := by intro a (ha : a ≤ a₀) rw [continuousWithinAt_iff_continuous_left_right] constructor @@ -688,13 +687,13 @@ theorem continuousOn_Iic_primitive_Iio [NoAtoms μ] {a₀ : ℝ} (hf : Integrabl continuousWithinAt_primitive (measure_singleton a) (by simpa [ha]) exact (continuousWithinAt_const.add h_cwa).congr h_split (h_split a (left_mem_Icc.2 ha)) -theorem continuousOn_Ici_primitive_Ici [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Ici a₀) μ) : - ContinuousOn (fun b ↦ ∫ x in Ici b, f x ∂μ) (Ici a₀) := by +theorem continuousOn_Ici_primitive_Ici [NullSingletonClass μ] {a₀ : ℝ} + (hf : IntegrableOn f (Ici a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Ici b, f x ∂μ) (Ici a₀) := by simp_rw [integral_Ici_eq_integral_Ioi] exact (hf.mono_set Ioi_subset_Ici_self).continuousOn_Ici_primitive_Ioi -theorem continuousOn_Iic_primitive_Iic [NoAtoms μ] {a₀ : ℝ} (hf : IntegrableOn f (Iic a₀) μ) : - ContinuousOn (fun b ↦ ∫ x in Iic b, f x ∂μ) (Iic a₀) := by +theorem continuousOn_Iic_primitive_Iic [NullSingletonClass μ] {a₀ : ℝ} + (hf : IntegrableOn f (Iic a₀) μ) : ContinuousOn (fun b ↦ ∫ x in Iic b, f x ∂μ) (Iic a₀) := by simp_rw [integral_Iic_eq_integral_Iio] exact (hf.mono_set Iio_subset_Iic_self).continuousOn_Iic_primitive_Iio diff --git a/Mathlib/MeasureTheory/Integral/ExpDecay.lean b/Mathlib/MeasureTheory/Integral/ExpDecay.lean index ddb9bf1a14abed..3f64e043d49cfb 100644 --- a/Mathlib/MeasureTheory/Integral/ExpDecay.lean +++ b/Mathlib/MeasureTheory/Integral/ExpDecay.lean @@ -6,7 +6,6 @@ Authors: David Loeffler module public import Mathlib.MeasureTheory.Integral.Asymptotics -public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic public import Mathlib.MeasureTheory.Integral.IntegralEqImproper /-! diff --git a/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean b/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean index d6a80a4a9e1495..1a9b39c030e3d8 100644 --- a/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean +++ b/Mathlib/MeasureTheory/Integral/FinMeasAdditive.lean @@ -322,6 +322,18 @@ theorem setToSimpleFunc_eq_sum_filter [DecidablePred fun x ↦ x ≠ (0 : F)] rw [hx0] exact map_zero _ +/-- The `setToSimpleFunc` is equal to a sum over any set that includes `f.range` (except `0`). -/ +theorem setToSimpleFunc_eq_sum_of_subset [DecidablePred fun x : F => x ≠ 0] + (T : Set α → F →L[ℝ] F') (hT : T ∅ = 0) {f : α →ₛ F} {s : Finset F} + (hs : {x ∈ f.range | x ≠ 0} ⊆ s) : + setToSimpleFunc T f = ∑ x ∈ s, T (f ⁻¹' {x}) x := by + rw [setToSimpleFunc_eq_sum_filter, Finset.sum_subset hs] + rintro x - hx; rw [Finset.mem_filter, not_and_or, Ne, Classical.not_not] at hx + rcases hx.symm with (rfl | hx) + · simp + rw [SimpleFunc.mem_range] at hx + rw [preimage_eq_empty] <;> simp [Set.disjoint_singleton_left, hx, hT] + theorem map_setToSimpleFunc (T : Set α → F →L[ℝ] F') (h_add : FinMeasAdditive μ T) {f : α →ₛ G} (hf : Integrable f μ) {g : G → F} (hg : g 0 = 0) : (f.map g).setToSimpleFunc T = ∑ x ∈ f.range, T (f ⁻¹' {x}) (g x) := by diff --git a/Mathlib/MeasureTheory/Integral/IntegrableOn.lean b/Mathlib/MeasureTheory/Integral/IntegrableOn.lean index 2c5825b528ca0f..f487a4f34cfd7c 100644 --- a/Mathlib/MeasureTheory/Integral/IntegrableOn.lean +++ b/Mathlib/MeasureTheory/Integral/IntegrableOn.lean @@ -850,7 +850,7 @@ theorem ContinuousOn.stronglyMeasurableAtFilter_nhdsWithin {α β : Type*} [Meas /-! ### Lemmas about adding and removing interval boundaries The primed lemmas take explicit arguments about the measure being finite at the endpoint, while -the unprimed ones use `[NoAtoms μ]`. +the unprimed ones use `[NullSingletonClass μ]`. -/ @@ -908,7 +908,7 @@ theorem integrableOn_Iic_iff_integrableOn_Iio' IntegrableOn f (Iic b) μ ↔ IntegrableOn f (Iio b) μ := by rw [← Iio_union_right, integrableOn_union, eq_true (integrableOn_singleton hb'), and_true] -variable [NoAtoms μ] +variable [NullSingletonClass μ] theorem integrableOn_Icc_iff_integrableOn_Ioc (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) : IntegrableOn f (Icc a b) μ ↔ IntegrableOn f (Ioc a b) μ := diff --git a/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean b/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean index a2553d7d8ab4cb..52175f6b0a4e31 100644 --- a/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean +++ b/Mathlib/MeasureTheory/Integral/IntegralEqImproper.lean @@ -5,9 +5,7 @@ Authors: Anatole Dedecker, Bhavik Mehta -/ module -public import Mathlib.Analysis.Calculus.Deriv.Support public import Mathlib.Analysis.SpecialFunctions.Pow.Deriv -public import Mathlib.MeasureTheory.Function.JacobianOneDim public import Mathlib.MeasureTheory.Integral.IntervalIntegral.IntegrationByParts public import Mathlib.MeasureTheory.Measure.Haar.NormedSpace public import Mathlib.MeasureTheory.Measure.Haar.Unique @@ -247,7 +245,7 @@ include ha hb in theorem aecover_Ioo_of_Ioc : AECover (μ.restrict <| Ioo A B) l fun i => Ioc (a i) (b i) := (aecover_Ioo_of_Ioo ha hb).superset (fun _ ↦ Ioo_subset_Ioc_self) fun _ ↦ measurableSet_Ioc -variable [NoAtoms μ] +variable [NullSingletonClass μ] theorem aecover_Ioc_of_Icc (ha : Tendsto a l (𝓝 A)) (hb : Tendsto b l (𝓝 B)) : AECover (μ.restrict <| Ioc A B) l fun i => Icc (a i) (b i) := diff --git a/Mathlib/MeasureTheory/Integral/IntervalAverage.lean b/Mathlib/MeasureTheory/Integral/IntervalAverage.lean index 1e35b907617a8d..455b155d94887c 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalAverage.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalAverage.lean @@ -19,7 +19,7 @@ formulas for this average: * `interval_average_eq_div`: `⨍ x in a..b, f x = (∫ x in a..b, f x) / (b - a)`; * `exists_eq_interval_average_of_measure`: `∃ c ∈ Ι a b, f c = ⨍ x in Ι a b, f x ∂μ`. -* `exists_eq_interval_average_of_noAtoms`: +* `exists_eq_interval_average_of_nullSingletonClass`: `∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ`. * `exists_eq_interval_average`: `∃ c ∈ uIoo a b, f c = ⨍ x in a..b, f x`. @@ -78,10 +78,10 @@ theorem exists_eq_interval_average_of_measure isCompact_uIcc measurableSet_uIoc uIoc_subset_uIcc hμfin) hμfin hμ0 /-- If `f : ℝ → ℝ` is continuous on `uIcc a b`, the interval has finite and nonzero `μ`-measure, -and `μ` has no atoms, then `∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ`. -/ -theorem exists_eq_interval_average_of_noAtoms - [NoAtoms μ] (hf : ContinuousOn f (uIcc a b)) (hμfin : μ (Ι a b) ≠ ⊤) (hμ0 : μ (Ι a b) ≠ 0) : - ∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ := by +and `μ` has value zero on singletons, then `∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ`. -/ +theorem exists_eq_interval_average_of_nullSingletonClass + [NullSingletonClass μ] (hf : ContinuousOn f (uIcc a b)) (hμfin : μ (Ι a b) ≠ ⊤) + (hμ0 : μ (Ι a b) ≠ 0) : ∃ c ∈ uIoo a b, f c = ⨍ x in Ι a b, f x ∂μ := by have hint : IntegrableOn f (Ι a b) μ := hf.integrableOn_of_subset_isCompact isCompact_uIcc measurableSet_uIoc uIoc_subset_uIcc hμfin have h : a ≠ b := by intro hab; simp [hab] at hμ0 @@ -95,10 +95,14 @@ theorem exists_eq_interval_average_of_noAtoms (hint.mono_set hs') (measure_ne_top_of_subset hs' hμfin) hμ0' exact ⟨c, hc, by rwa [← setAverage_congr hs_ev]⟩ +@[deprecated (since := "2026-06-09")] +alias exists_eq_interval_average_of_noAtoms := exists_eq_interval_average_of_nullSingletonClass + /-- The mean value theorem for integrals: There exists a point in an interval such that the mean of a continuous function over the interval equals the value of the function at the point. -/ theorem exists_eq_interval_average (hab : a ≠ b) (hf : ContinuousOn f (uIcc a b)) : ∃ c ∈ uIoo a b, f c = ⨍ x in a..b, f x := - exists_eq_interval_average_of_noAtoms hf (by simp) (by simpa using sub_ne_zero.mpr hab.symm) + exists_eq_interval_average_of_nullSingletonClass hf (by simp) + (by simpa using sub_ne_zero.mpr hab.symm) diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/AbsolutelyContinuousFun.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/AbsolutelyContinuousFun.lean index 463190f5939235..f3dc5eb2192ae7 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/AbsolutelyContinuousFun.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/AbsolutelyContinuousFun.lean @@ -6,7 +6,6 @@ Authors: Yizheng Zhu module public import Mathlib.Algebra.BigOperators.Group.Finset.Gaps -public import Mathlib.Analysis.Calculus.Deriv.Mul public import Mathlib.MeasureTheory.Integral.IntervalIntegral.DerivIntegrable public import Mathlib.MeasureTheory.Integral.IntervalIntegral.LebesgueDifferentiationThm diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean index 94ce88fa5001e0..e7b07212a97022 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Basic.lean @@ -94,7 +94,7 @@ theorem intervalIntegrable_congr_ae {g : ℝ → ε} (h : f =ᵐ[μ.restrict (Ι IntervalIntegrable f μ a b ↔ IntervalIntegrable g μ a b := by rw [intervalIntegrable_iff, integrableOn_congr_fun_ae h, intervalIntegrable_iff] -theorem intervalIntegrable_congr_uIoo [NoAtoms μ] {g : ℝ → ε} (h : EqOn f g (uIoo a b)) : +theorem intervalIntegrable_congr_uIoo [NullSingletonClass μ] {g : ℝ → ε} (h : EqOn f g (uIoo a b)) : IntervalIntegrable f μ a b ↔ IntervalIntegrable g μ a b := by apply intervalIntegrable_congr_ae rw [uIoc, ← restrict_Ioo_eq_restrict_Ioc] @@ -105,8 +105,8 @@ theorem IntervalIntegrable.congr_ae {g : ℝ → ε} (hf : IntervalIntegrable f IntervalIntegrable g μ a b := by rwa [← intervalIntegrable_congr_ae h] -theorem IntervalIntegrable.congr_uIoo [NoAtoms μ] {g : ℝ → ε} (hf : IntervalIntegrable f μ a b) - (h : EqOn f g (uIoo a b)) : IntervalIntegrable g μ a b := +theorem IntervalIntegrable.congr_uIoo [NullSingletonClass μ] {g : ℝ → ε} + (hf : IntervalIntegrable f μ a b) (h : EqOn f g (uIoo a b)) : IntervalIntegrable g μ a b := intervalIntegrable_congr_uIoo h |>.mp hf theorem intervalIntegrable_congr {g : ℝ → ε} (h : EqOn f g (Ι a b)) : @@ -116,13 +116,13 @@ theorem intervalIntegrable_congr {g : ℝ → ε} (h : EqOn f g (Ι a b)) : alias ⟨IntervalIntegrable.congr, _⟩ := intervalIntegrable_congr /-- Interval integrability is invariant when functions change along discrete sets. -/ -theorem IntervalIntegrable.congr_codiscreteWithin {g : ℝ → ε} [NoAtoms μ] +theorem IntervalIntegrable.congr_codiscreteWithin {g : ℝ → ε} [NullSingletonClass μ] (h : f =ᶠ[codiscreteWithin (Ι a b)] g) (hf : IntervalIntegrable f μ a b) : IntervalIntegrable g μ a b := hf.congr_ae (ae_restrict_le_codiscreteWithin measurableSet_Ioc h) /-- Interval integrability is invariant when functions change along discrete sets. -/ -theorem intervalIntegrable_congr_codiscreteWithin {g : ℝ → ε} [NoAtoms μ] +theorem intervalIntegrable_congr_codiscreteWithin {g : ℝ → ε} [NullSingletonClass μ] (h : f =ᶠ[codiscreteWithin (Ι a b)] g) : IntervalIntegrable f μ a b ↔ IntervalIntegrable g μ a b := ⟨(IntervalIntegrable.congr_codiscreteWithin h ·), @@ -132,22 +132,22 @@ theorem intervalIntegrable_iff_integrableOn_Ioc_of_le (hab : a ≤ b) : IntervalIntegrable f μ a b ↔ IntegrableOn f (Ioc a b) μ := by rw [intervalIntegrable_iff, uIoc_of_le hab] -theorem intervalIntegrable_iff' [NoAtoms μ] (h : ‖f (min a b)‖ₑ ≠ ∞ := by finiteness) : +theorem intervalIntegrable_iff' [NullSingletonClass μ] (h : ‖f (min a b)‖ₑ ≠ ∞ := by finiteness) : IntervalIntegrable f μ a b ↔ IntegrableOn f (uIcc a b) μ := by rw [intervalIntegrable_iff, ← Icc_min_max, uIoc, integrableOn_Icc_iff_integrableOn_Ioc h] -theorem intervalIntegrable_iff_integrableOn_Icc_of_le [NoAtoms μ] +theorem intervalIntegrable_iff_integrableOn_Icc_of_le [NullSingletonClass μ] (hab : a ≤ b) (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) : IntervalIntegrable f μ a b ↔ IntegrableOn f (Icc a b) μ := by rw [intervalIntegrable_iff_integrableOn_Ioc_of_le hab, integrableOn_Icc_iff_integrableOn_Ioc ha] -theorem intervalIntegrable_iff_integrableOn_Ico_of_le [NoAtoms μ] +theorem intervalIntegrable_iff_integrableOn_Ico_of_le [NullSingletonClass μ] (hab : a ≤ b) (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) (hb : ‖f b‖ₑ ≠ ∞ := by finiteness) : IntervalIntegrable f μ a b ↔ IntegrableOn f (Ico a b) μ := by rw [intervalIntegrable_iff_integrableOn_Icc_of_le hab ha, integrableOn_Icc_iff_integrableOn_Ico hb] -theorem intervalIntegrable_iff_integrableOn_Ioo_of_le [NoAtoms μ] +theorem intervalIntegrable_iff_integrableOn_Ioo_of_le [NullSingletonClass μ] (hab : a ≤ b) (ha : ‖f a‖ₑ ≠ ∞ := by finiteness) (hb : ‖f b‖ₑ ≠ ∞ := by finiteness) : IntervalIntegrable f μ a b ↔ IntegrableOn f (Ioo a b) μ := by rw [intervalIntegrable_iff_integrableOn_Icc_of_le hab ha, @@ -171,10 +171,9 @@ theorem intervalIntegrable_const_iff {c : ε} (hc : ‖c‖ₑ ≠ ⊤ := by fin simp [intervalIntegrable_iff, integrableOn_const_iff hc] @[simp] -theorem intervalIntegrable_const [IsLocallyFiniteMeasure μ] - {c : E} (hc : ‖c‖ₑ ≠ ⊤ := by finiteness) : +theorem intervalIntegrable_const [IsLocallyFiniteMeasure μ] {c : E} : IntervalIntegrable (fun _ => c) μ a b := - intervalIntegrable_const_iff hc |>.2 <| Or.inr measure_Ioc_lt_top + intervalIntegrable_const_iff (by simp) |>.2 <| Or.inr measure_Ioc_lt_top protected theorem IntervalIntegrable.zero : IntervalIntegrable (0 : ℝ → E) μ a b := (intervalIntegrable_const_iff <| by finiteness).mpr <| .inl rfl @@ -1188,7 +1187,7 @@ theorem integral_Iio_sub_Iio (hf : IntegrableOn f (Iio b) μ) (hab : a ≤ b) : rw [sub_eq_iff_eq_add', ← setIntegral_union (by grind) measurableSet_Ico ha h, Iio_union_Ico_eq_Iio hab] -theorem integral_Iio_sub_Iio' [NoAtoms μ] (hf : IntegrableOn f (Iio b) μ) +theorem integral_Iio_sub_Iio' [NullSingletonClass μ] (hf : IntegrableOn f (Iio b) μ) (hg : IntegrableOn f (Iio a) μ) : ∫ x in Iio b, f x ∂μ - ∫ x in Iio a, f x ∂μ = ∫ x in a..b, f x ∂μ := by wlog! hab : a ≤ b generalizing a b @@ -1202,7 +1201,7 @@ theorem integral_Ici_sub_Ici (hf : IntegrableOn f (Ici a) μ) (hab : a ≤ b) : rw [sub_eq_iff_eq_add', ← setIntegral_union (by grind) measurableSet_Ico ha h, union_comm, Ico_union_Ici_eq_Ici hab] -theorem integral_Ici_sub_Ici' [NoAtoms μ] (hf : IntegrableOn f (Ici a) μ) +theorem integral_Ici_sub_Ici' [NullSingletonClass μ] (hf : IntegrableOn f (Ici a) μ) (hg : IntegrableOn f (Ici b) μ) : ∫ x in Ici a, f x ∂μ - ∫ x in Ici b, f x ∂μ = ∫ x in a..b, f x ∂μ := by wlog! hab : a ≤ b generalizing a b @@ -1243,13 +1242,13 @@ theorem integral_congr_ae (h : ∀ᵐ x ∂μ, x ∈ Ι a b → f x = g x) : ∫ x in a..b, f x ∂μ = ∫ x in a..b, g x ∂μ := integral_congr_ae' (ae_uIoc_iff.mp h).1 (ae_uIoc_iff.mp h).2 -theorem integral_congr_uIoo [NoAtoms μ] (h : (uIoo a b).EqOn f g) : +theorem integral_congr_uIoo [NullSingletonClass μ] (h : (uIoo a b).EqOn f g) : ∫ x in a..b, f x ∂μ = ∫ x in a..b, g x ∂μ := by apply integral_congr_ae filter_upwards [μ.ae_ne <| a ⊔ b] with x _ hx exact h ⟨hx.left, lt_of_le_of_ne hx.right ‹_›⟩ -theorem integral_congr_Ioo_of_le [NoAtoms μ] (hab : a ≤ b) (h : (Ioo a b).EqOn f g) : +theorem integral_congr_Ioo_of_le [NullSingletonClass μ] (hab : a ≤ b) (h : (Ioo a b).EqOn f g) : ∫ x in a..b, f x ∂μ = ∫ x in a..b, g x ∂μ := integral_congr_uIoo <| uIoo_of_le hab ▸ h @@ -1430,7 +1429,7 @@ theorem integral_mono_on (h : ∀ x ∈ Icc a b, f x ≤ g x) : let H x hx := h x <| Ioc_subset_Icc_self hx simpa only [integral_of_le hab] using setIntegral_mono_on hf.1 hg.1 measurableSet_Ioc H -theorem integral_mono_on_of_le_Ioo [NoAtoms μ] (h : ∀ x ∈ Ioo a b, f x ≤ g x) : +theorem integral_mono_on_of_le_Ioo [NullSingletonClass μ] (h : ∀ x ∈ Ioo a b, f x ≤ g x) : (∫ u in a..b, f u ∂μ) ≤ ∫ u in a..b, g u ∂μ := by simp only [integral_of_le hab, integral_Ioc_eq_integral_Ioo] apply setIntegral_mono_on diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/DerivIntegrable.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/DerivIntegrable.lean index c7b202caf80b38..2c076ad33ed806 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/DerivIntegrable.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/DerivIntegrable.lean @@ -5,7 +5,6 @@ Authors: Yizheng Zhu -/ module -public import Mathlib.Analysis.BoundedVariation public import Mathlib.MeasureTheory.Function.AbsolutelyContinuous public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Slope import Mathlib.Algebra.Order.Interval.Set.Group diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/DistLEIntegral.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/DistLEIntegral.lean index 734ba83cf45c3e..19e966083ae3d0 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/DistLEIntegral.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/DistLEIntegral.lean @@ -5,14 +5,12 @@ Authors: Yury G. Kudryashov -/ module -public import Mathlib.Analysis.Calculus.Deriv.Basic public import Mathlib.Analysis.Calculus.DiffContOnCl public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic public import Mathlib.Analysis.Calculus.LineDeriv.Basic import Mathlib.Analysis.Calculus.MeanValue import Mathlib.MeasureTheory.Integral.IntervalIntegral.FundThmCalculus -import Mathlib.Analysis.Normed.Module.Completion /-! # Displacement is at most the integral of the speed diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean index 540bba846097fc..4faffca6cfa62f 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/FundThmCalculus.lean @@ -5,10 +5,8 @@ Authors: Yury Kudryashov, Patrick Massot, Sébastien Gouëzel -/ module -public import Mathlib.Analysis.Calculus.Deriv.Add public import Mathlib.Analysis.Calculus.Deriv.Comp public import Mathlib.Analysis.Calculus.FDeriv.Measurable -public import Mathlib.Analysis.Normed.Module.Dual public import Mathlib.MeasureTheory.Integral.Bochner.FundThmCalculus public import Mathlib.MeasureTheory.Integral.Bochner.VitaliCaratheodory public import Mathlib.MeasureTheory.Integral.DominatedConvergence @@ -1098,8 +1096,8 @@ theorem integral_le_sub_of_hasDeriv_right_of_le (hab : a ≤ b) (hcont : Continu (hφg : ∀ x ∈ Ioo a b, φ x ≤ g' x) : (∫ y in a..b, φ y) ≤ g b - g a := by rw [← neg_le_neg_iff] convert! - sub_le_integral_of_hasDeriv_right_of_le hab hcont.neg (fun x hx => (hderiv x hx).neg) φint.neg - fun x hx => neg_le_neg (hφg x hx) using 1 + sub_le_integral_of_hasDeriv_right_of_le hab hcont.fun_neg (fun x hx => (hderiv x hx).neg) + φint.neg fun x hx => neg_le_neg (hφg x hx) using 1 · abel · simp only [← integral_neg]; rfl diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean index 7e7ca233026342..358f01e9d06fb1 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/IntegrationByParts.lean @@ -7,6 +7,7 @@ module public import Mathlib.MeasureTheory.Function.JacobianOneDim public import Mathlib.MeasureTheory.Integral.IntervalIntegral.FundThmCalculus +public import Mathlib.Tactic.CrossRefAttribute /-! # Integration by parts and by substitution @@ -526,6 +527,7 @@ theorem integral_comp_mul_deriv' {f f' g : ℝ → ℝ} (h : ∀ x ∈ uIcc a b, and `g` is continuous, then we can substitute `u = f x` to get `∫ x in a..b, (g ∘ f) x * f' x = ∫ u in f a..f b, g u`. -/ +@[wikidata Q1071270] theorem integral_comp_mul_deriv {f f' g : ℝ → ℝ} (h : ∀ x ∈ uIcc a b, HasDerivAt f (f' x) x) (h' : ContinuousOn f' (uIcc a b)) (hg : Continuous g) : (∫ x in a..b, (g ∘ f) x * f' x) = ∫ x in f a..f b, g x := diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean index 2654b47e2e7e3b..23dad80410b6b8 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/LebesgueDifferentiationThm.lean @@ -15,11 +15,11 @@ public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic This file proves the interval version of the Lebesgue Differentiation Theorem. There are two versions in this file. -* `LocallyIntegrable.ae_hasDerivAt_integral` is the global version. It states that if `f : ℝ → ℝ` - is locally integrable, then for almost every `x`, for any `c : ℝ`, the derivative of - `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`. +* `LocallyIntegrable.ae_hasDerivAt_integral` is the global version. It states that if `f : ℝ → E` + is locally integrable (`E` a Banach space), then for almost every `x`, for any `c : ℝ`, the + derivative of `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`. -* `IntervalIntegrable.ae_hasDerivAt_integral` is the local version. It states that if `f : ℝ → ℝ` +* `IntervalIntegrable.ae_hasDerivAt_integral` is the local version. It states that if `f : ℝ → E` is interval integrable on `a..b`, then for almost every `x ∈ uIcc a b`, for any `c ∈ uIcc a b`, the derivative of `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`. -/ @@ -30,10 +30,12 @@ open MeasureTheory Set Filter Function IsUnifLocDoublingMeasure open scoped Topology -/-- The (global) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → ℝ` is +variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E] + +/-- The (global) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → E` is locally integrable, then for almost every `x`, for any `c : ℝ`, the derivative of `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`. -/ -theorem LocallyIntegrable.ae_hasDerivAt_integral {f : ℝ → ℝ} (hf : LocallyIntegrable f volume) : +theorem LocallyIntegrable.ae_hasDerivAt_integral {f : ℝ → E} (hf : LocallyIntegrable f volume) : ∀ᵐ x, ∀ c, HasDerivAt (fun x => ∫ (t : ℝ) in c..x, f t) (f x) x := by have hg (x y : ℝ) : IntervalIntegrable f volume x y := intervalIntegrable_iff.mpr <| @@ -48,24 +50,24 @@ theorem LocallyIntegrable.ae_hasDerivAt_integral {f : ℝ → ℝ} (hf : Locally · refine Filter.tendsto_congr' ?_ |>.mpr (hx.comp x.tendsto_Icc_vitaliFamily_left) filter_upwards [self_mem_nhdsWithin] with y hy replace hy : y ≤ x := hy.le - suffices -((y - x)⁻¹ * ∫ (t : ℝ) in Icc y x, f t) = (x - y)⁻¹ * ∫ (t : ℝ) in Icc y x, f t by + suffices -((y - x)⁻¹ • ∫ (t : ℝ) in Icc y x, f t) = (x - y)⁻¹ • ∫ (t : ℝ) in Icc y x, f t by simpa [slope, average, intervalIntegral.integral_interval_sub_left, hg, intervalIntegral.integral_of_ge, hy, h] - rw [← neg_mul, neg_inv, neg_sub] + rw [← neg_smul, neg_inv, neg_sub] · refine Filter.tendsto_congr' ?_ |>.mpr (hx.comp x.tendsto_Icc_vitaliFamily_right) filter_upwards [self_mem_nhdsWithin] with y hy replace hy : x ≤ y := hy.le simp [slope, average, intervalIntegral.integral_interval_sub_left, hg, intervalIntegral.integral_of_le, hy, h] -/-- The (local) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → ℝ` is +/-- The (local) interval version of the *Lebesgue Differentiation Theorem*: if `f : ℝ → E` is interval integrable on `a..b`, then for almost every `x ∈ uIcc a b`, for any `c ∈ uIcc a b`, the derivative of `∫ (t : ℝ) in c..x, f t` at `x` is equal to `f x`. -/ -theorem IntervalIntegrable.ae_hasDerivAt_integral {f : ℝ → ℝ} {a b : ℝ} +theorem IntervalIntegrable.ae_hasDerivAt_integral {f : ℝ → E} {a b : ℝ} (hf : IntervalIntegrable f volume a b) : ∀ᵐ x, x ∈ uIcc a b → ∀ c ∈ uIcc a b, HasDerivAt (fun x => ∫ (t : ℝ) in c..x, f t) (f x) x := by wlog hab : a ≤ b - · exact uIcc_comm b a ▸ @this f b a hf.symm (by linarith) + · exact uIcc_comm b a ▸ this hf.symm (by linarith) rw [uIcc_of_le hab] have h₁ : ∀ᵐ x, x ≠ a := by simp [ae_iff, measure_singleton] have h₂ : ∀ᵐ x, x ≠ b := by simp [ae_iff, measure_singleton] diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Periodic.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Periodic.lean index 1658960da401b1..dfcd118af50d5e 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/Periodic.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/Periodic.lean @@ -7,9 +7,7 @@ module public import Mathlib.MeasureTheory.Measure.Lebesgue.EqHaar public import Mathlib.MeasureTheory.Measure.Haar.Quotient -public import Mathlib.MeasureTheory.Integral.IntervalIntegral.Basic public import Mathlib.Topology.Algebra.Order.Floor -public import Mathlib.Topology.Instances.AddCircle.Real /-! # Integrals of periodic functions diff --git a/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean b/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean index 243d55e8c4436f..0fe73938b60340 100644 --- a/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean +++ b/Mathlib/MeasureTheory/Integral/IntervalIntegral/TrapezoidalRule.lean @@ -6,6 +6,7 @@ Authors: P. Michael Kielstra module public import Mathlib.Analysis.SpecialFunctions.Integrals.Basic +public import Mathlib.Tactic.CrossRefAttribute public import Mathlib.Tactic.Field /-! @@ -29,6 +30,7 @@ open MeasureTheory intervalIntegral Interval Finset HasDerivWithinAt Set /-- Integration of `f` from `a` to `b` using the trapezoidal rule with `N+1` total evaluations of `f`. (Note the off-by-one problem here: `N` counts the number of trapezoids, not the number of evaluations.) -/ +@[wikidata Q833293] noncomputable def trapezoidal_integral (f : ℝ → ℝ) (N : ℕ) (a b : ℝ) : ℝ := ((b - a) / N) * ((f a + f b) / 2 + ∑ k ∈ range (N - 1), f (a + (k + 1) * (b - a) / N)) diff --git a/Mathlib/MeasureTheory/Integral/Layercake.lean b/Mathlib/MeasureTheory/Integral/Layercake.lean index 89d87338d77988..59e6abe4dd4696 100644 --- a/Mathlib/MeasureTheory/Integral/Layercake.lean +++ b/Mathlib/MeasureTheory/Integral/Layercake.lean @@ -27,7 +27,7 @@ The essence of the (mathematical) proof is Fubini's theorem. We also give the most common application of the layer cake formula - a representation of the integral of a nonnegative function f: -$$∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) ≥ t} dt$$ +$$∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) ≥ t\} \,dt$$ Variants of the formulas with measures of sets of the form `{ω | f(ω) > t}` instead of `{ω | f(ω) ≥ t}` are also included. @@ -41,12 +41,12 @@ Variants of the formulas with measures of sets of the form `{ω | f(ω) > t}` in * `MeasureTheory.lintegral_eq_lintegral_meas_le` and `MeasureTheory.lintegral_eq_lintegral_meas_lt`: The most common special cases of the layer cake formulas, stating that for a nonnegative - function f we have $∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) ≥ t} dt$ and - $∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) > t} dt$, respectively. + function f we have $∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) ≥ t\} \,dt$ and + $∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) > t\} \,dt$, respectively. * `Integrable.integral_eq_integral_meas_lt`: A Bochner integral version of the most common special case of the layer cake formulas, stating that for an integrable and a.e.-nonnegative function f we have - $∫ f(ω) ∂μ(ω) = ∫ μ {ω | f(ω) > t} dt$. + $∫ f(ω) \,∂μ(ω) = ∫ μ \{ω \mid f(ω) > t\} \,dt$. ## See also @@ -84,7 +84,7 @@ theorem countable_meas_le_ne_meas_lt (g : α → R) : exact ⟨μ {a | t < g a}, this, fun s hs ↦ measure_mono (fun a ha ↦ hs.trans_le ha)⟩ theorem meas_le_ae_eq_meas_lt {R : Type*} [LinearOrder R] [MeasurableSpace R] - (ν : Measure R) [NoAtoms ν] (g : α → R) : + (ν : Measure R) [NullSingletonClass ν] (g : α → R) : (fun t => μ {a : α | t ≤ g a}) =ᵐ[ν] fun t => μ {a : α | t < g a} := Set.Countable.measure_zero (countable_meas_le_ne_meas_lt μ g) _ diff --git a/Mathlib/MeasureTheory/Integral/MeanInequalities.lean b/Mathlib/MeasureTheory/Integral/MeanInequalities.lean index f2410ebba2583e..719a35fba6e1e0 100644 --- a/Mathlib/MeasureTheory/Integral/MeanInequalities.lean +++ b/Mathlib/MeasureTheory/Integral/MeanInequalities.lean @@ -338,7 +338,7 @@ theorem lintegral_rpow_add_le_add_eLpNorm_mul_lintegral_rpow_add {p q : ℝ} ∫⁻ a : α, (f a + g a) * (f + g) a ^ (p - 1) ∂μ := rfl simp_rw [h_add_apply, add_mul] - rw [lintegral_add_left' (hf.mul h_add_m)] + rw [lintegral_add_left' (hf.fun_mul h_add_m)] _ ≤ ((∫⁻ a, f a ^ p ∂μ) ^ (1 / p) + (∫⁻ a, g a ^ p ∂μ) ^ (1 / p)) * (∫⁻ a, (f a + g a) ^ p ∂μ) ^ (1 / q) := by diff --git a/Mathlib/MeasureTheory/Integral/Prod.lean b/Mathlib/MeasureTheory/Integral/Prod.lean index 6dd14fda021671..db1b5939973c22 100644 --- a/Mathlib/MeasureTheory/Integral/Prod.lean +++ b/Mathlib/MeasureTheory/Integral/Prod.lean @@ -7,8 +7,6 @@ module public import Mathlib.MeasureTheory.Function.LpSeminorm.Prod public import Mathlib.MeasureTheory.Integral.DominatedConvergence -public import Mathlib.MeasureTheory.Integral.Bochner.Set -public import Mathlib.MeasureTheory.Measure.Prod /-! # Integration with respect to the product measure @@ -37,7 +35,6 @@ product measure, Fubini's theorem, Fubini-Tonelli theorem public section - noncomputable section open scoped Topology ENNReal MeasureTheory @@ -60,12 +57,6 @@ functions. We show that if `f` is a binary measurable function, then the functio along one of the variables (using either the Lebesgue or Bochner integral) is measurable. -/ - -theorem measurableSet_integrable [SFinite ν] ⦃f : α → β → E⦄ - (hf : StronglyMeasurable (uncurry f)) : MeasurableSet {x | Integrable (f x) ν} := by - simp_rw [Integrable, hf.of_uncurry_left.aestronglyMeasurable, true_and] - exact measurableSet_lt (Measurable.lintegral_prod_right hf.enorm) measurable_const - section variable [NormedSpace ℝ E] @@ -75,49 +66,10 @@ variable [NormedSpace ℝ E] This version has `f` in curried form. -/ theorem MeasureTheory.StronglyMeasurable.integral_prod_right [SFinite ν] ⦃f : α → β → E⦄ (hf : StronglyMeasurable (uncurry f)) : StronglyMeasurable fun x => ∫ y, f x y ∂ν := by - classical - by_cases hE : CompleteSpace E; swap; · simp [integral, hE, stronglyMeasurable_const] - borelize E - haveI : SeparableSpace (range (uncurry f) ∪ {0} : Set E) := - hf.separableSpace_range_union_singleton - let s : ℕ → SimpleFunc (α × β) E := - SimpleFunc.approxOn _ hf.measurable (range (uncurry f) ∪ {0}) 0 (by simp) - let s' : ℕ → α → SimpleFunc β E := fun n x => (s n).comp (Prod.mk x) measurable_prodMk_left - let f' : ℕ → α → E := fun n => {x | Integrable (f x) ν}.indicator fun x => (s' n x).integral ν - have hf' : ∀ n, StronglyMeasurable (f' n) := by - intro n; refine StronglyMeasurable.indicator ?_ (measurableSet_integrable hf) - have : ∀ x, ((s' n x).range.filter fun x => x ≠ 0) ⊆ (s n).range := by - intro x; refine Finset.Subset.trans (Finset.filter_subset _ _) ?_; intro y - simp_rw [SimpleFunc.mem_range]; rintro ⟨z, rfl⟩; exact ⟨(x, z), rfl⟩ - simp only [SimpleFunc.integral_eq_sum_of_subset (this _)] - refine Finset.stronglyMeasurable_fun_sum _ fun x _ => ?_ - refine (Measurable.ennreal_toReal ?_).stronglyMeasurable.smul_const _ - simp only [s', SimpleFunc.coe_comp, preimage_comp] - apply measurable_measure_prodMk_left - exact (s n).measurableSet_fiber x - have h2f' : Tendsto f' atTop (𝓝 fun x : α => ∫ y : β, f x y ∂ν) := by - rw [tendsto_pi_nhds]; intro x - by_cases hfx : Integrable (f x) ν - · have (n : _) : Integrable (s' n x) ν := by - apply (hfx.norm.add hfx.norm).mono' (s' n x).aestronglyMeasurable - filter_upwards with y - simp_rw [s', SimpleFunc.coe_comp]; exact SimpleFunc.norm_approxOn_zero_le _ _ (x, y) n - simp only [f', hfx, SimpleFunc.integral_eq_integral _ (this _), indicator_of_mem, - mem_setOf_eq] - refine - tendsto_integral_of_dominated_convergence (fun y => ‖f x y‖ + ‖f x y‖) - (fun n => (s' n x).aestronglyMeasurable) (hfx.norm.add hfx.norm) ?_ ?_ - · refine fun n => Eventually.of_forall fun y => - SimpleFunc.norm_approxOn_zero_le ?_ ?_ (x, y) n - · exact hf.measurable - · simp - · refine Eventually.of_forall fun y => SimpleFunc.tendsto_approxOn ?_ ?_ ?_ - · exact hf.measurable.of_uncurry_left - · simp - apply subset_closure - simp [-uncurry_apply_pair] - · simp [f', hfx, integral_undef] - exact stronglyMeasurable_of_tendsto _ hf' h2f' + simp only [integral_eq_setToFun] + apply StronglyMeasurable.setToFun_prod_right _ (fun s hs ↦ ?_) hf + refine (Measurable.ennreal_toReal ?_).stronglyMeasurable.smul_const _ + exact measurable_measure_prodMk_left hs /-- The Bochner integral is measurable. This shows that the integrand of (the right-hand-side of) Fubini's theorem is measurable. -/ @@ -380,13 +332,11 @@ end variable [NormedSpace ℝ E] theorem Integrable.integral_prod_left ⦃f : α × β → E⦄ (hf : Integrable f (μ.prod ν)) : - Integrable (fun x => ∫ y, f (x, y) ∂ν) μ := - Integrable.mono hf.integral_norm_prod_left hf.aestronglyMeasurable.integral_prod_right' <| - Eventually.of_forall fun x => - (norm_integral_le_integral_norm _).trans_eq <| - (norm_of_nonneg <| - integral_nonneg_of_ae <| - Eventually.of_forall fun y => (norm_nonneg (f (x, y)) :)).symm + Integrable (fun x => ∫ y, f (x, y) ∂ν) μ := by + apply Integrable.mono hf.integral_norm_prod_left hf.aestronglyMeasurable.integral_prod_right' + filter_upwards with x + grw [norm_integral_le_integral_norm] + exact le_abs_self _ theorem Integrable.integral_prod_right [SFinite μ] ⦃f : α × β → E⦄ (hf : Integrable f (μ.prod ν)) : Integrable (fun y => ∫ x, f (x, y) ∂μ) ν := diff --git a/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Real.lean b/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Real.lean index f7835beb238390..1ce3bc6e7b5db6 100644 --- a/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Real.lean +++ b/Mathlib/MeasureTheory/Integral/RieszMarkovKakutani/Real.lean @@ -8,7 +8,6 @@ module public import Mathlib.MeasureTheory.Integral.Bochner.Set public import Mathlib.MeasureTheory.Integral.CompactlySupported public import Mathlib.MeasureTheory.Integral.RieszMarkovKakutani.Basic -public import Mathlib.MeasureTheory.Measure.Regular public import Mathlib.Order.Interval.Set.Union /-! diff --git a/Mathlib/MeasureTheory/Integral/SetToL1.lean b/Mathlib/MeasureTheory/Integral/SetToL1.lean index 1af06506765fed..bd8c0a097c415a 100644 --- a/Mathlib/MeasureTheory/Integral/SetToL1.lean +++ b/Mathlib/MeasureTheory/Integral/SetToL1.lean @@ -844,6 +844,12 @@ theorem setToFun_simpleFunc [CompleteSpace F] (hT : DominatedFinMeasAdditive μ apply (SimpleFunc.setToSimpleFunc_congr T (fun s ↦ hT.eq_zero_of_measure_zero) hT.1 hf _).symm grw [A, Lp.simpleFunc.toSimpleFunc_eq_toFun] +theorem setToFun_simpleFunc_eq_setToSimpleFunc [CompleteSpace F] + (hT : DominatedFinMeasAdditive μ T C) (f : SimpleFunc α E) (hf : Integrable f μ) : + setToFun μ T hT f = f.setToSimpleFunc T := by + rw [setToFun_simpleFunc hT f hf] + rfl + section Order variable {G' G'' : Type*} @@ -1384,6 +1390,66 @@ theorem tendsto_setToFun_filter_of_norm_le_const (hT : DominatedFinMeasAdditive exact tendsto_setToFun_filter_of_dominated_convergence hT C h_meas h_boundc (integrable_const c) h_lim +omit [NormedSpace ℝ E] in +theorem _root_.measurableSet_integrable {β : Type*} {mβ : MeasurableSpace β} [SFinite μ] + ⦃f : β → α → E⦄ (hf : StronglyMeasurable (Function.uncurry f)) : + MeasurableSet {x | Integrable (f x) μ} := by + simp_rw [Integrable, hf.of_uncurry_left.aestronglyMeasurable, true_and] + exact measurableSet_lt (Measurable.lintegral_prod_right hf.enorm) measurable_const + +/-- The `setToFun` operation is measurable. This shows that the integrand of (the right-hand-side +of) Fubini's theorem is measurable. This version has `f` in curried form. -/ +theorem StronglyMeasurable.setToFun_prod_right {β : Type*} {mβ : MeasurableSpace β} [SFinite μ] + (hT : DominatedFinMeasAdditive μ T C) + (h'T : ∀ (s : Set (β × α)), MeasurableSet s → StronglyMeasurable fun x => T (Prod.mk x ⁻¹' s)) + ⦃f : β → α → E⦄ (hf : StronglyMeasurable (Function.uncurry f)) : + StronglyMeasurable fun x => setToFun μ T hT (f x) := by + classical + by_cases hF : CompleteSpace F; swap; + · simp [setToFun, hF, stronglyMeasurable_const] + borelize E + haveI : SeparableSpace (range (Function.uncurry f) ∪ {0} : Set E) := + hf.separableSpace_range_union_singleton + let s : ℕ → SimpleFunc (β × α) E := + SimpleFunc.approxOn _ hf.measurable (range (Function.uncurry f) ∪ {0}) 0 (by simp) + let s' : ℕ → β → SimpleFunc α E := fun n x => (s n).comp (Prod.mk x) measurable_prodMk_left + let f' : ℕ → β → F := fun n => + {x | Integrable (f x) μ}.indicator fun x => (s' n x).setToSimpleFunc T + have hf' n : StronglyMeasurable (f' n) := by + refine StronglyMeasurable.indicator ?_ (measurableSet_integrable hf) + have : ∀ x, ((s' n x).range.filter fun x => x ≠ 0) ⊆ (s n).range := by + intro x; refine Finset.Subset.trans (Finset.filter_subset _ _) ?_; intro y + simp_rw [SimpleFunc.mem_range]; rintro ⟨z, rfl⟩; exact ⟨(x, z), rfl⟩ + simp_rw [SimpleFunc.setToSimpleFunc_eq_sum_of_subset T hT.1.map_empty_eq_zero (this _)] + refine Finset.stronglyMeasurable_fun_sum _ fun x _ => ?_ + simp only [s', SimpleFunc.coe_comp, preimage_comp] + apply StronglyMeasurable.apply_continuousLinearMap + apply h'T + exact (s n).measurableSet_fiber x + have h2f' : Tendsto f' atTop (𝓝 fun x : β => setToFun μ T hT (f x)) := by + apply tendsto_pi_nhds.2 fun x ↦ ?_ + by_cases hfx : Integrable (f x) μ + · have (n : _) : Integrable (s' n x) μ := by + apply (hfx.norm.add hfx.norm).mono' (s' n x).aestronglyMeasurable + filter_upwards with y + simp_rw [s', SimpleFunc.coe_comp]; exact SimpleFunc.norm_approxOn_zero_le _ _ (x, y) n + simp only [mem_setOf_eq, hfx, indicator_of_mem, this, + ← setToFun_simpleFunc_eq_setToSimpleFunc hT, f'] + refine + tendsto_setToFun_of_dominated_convergence hT (fun y => ‖f x y‖ + ‖f x y‖) + (fun n => (s' n x).aestronglyMeasurable) (hfx.norm.add hfx.norm) ?_ ?_ + · refine fun n => Eventually.of_forall fun y => + SimpleFunc.norm_approxOn_zero_le ?_ ?_ (x, y) n + · exact hf.measurable + · simp + · refine Eventually.of_forall fun y => SimpleFunc.tendsto_approxOn ?_ ?_ ?_ + · exact hf.measurable.of_uncurry_left + · simp + apply subset_closure + simp [-Function.uncurry_apply_pair] + · simp [f', hfx, setToFun_undef] + exact stronglyMeasurable_of_tendsto _ hf' h2f' + variable {X : Type*} [TopologicalSpace X] [FirstCountableTopology X] theorem continuousWithinAt_setToFun_of_dominated (hT : DominatedFinMeasAdditive μ T C) diff --git a/Mathlib/MeasureTheory/Integral/TorusIntegral.lean b/Mathlib/MeasureTheory/Integral/TorusIntegral.lean index e008fbecf59ce4..02860db94b2dec 100644 --- a/Mathlib/MeasureTheory/Integral/TorusIntegral.lean +++ b/Mathlib/MeasureTheory/Integral/TorusIntegral.lean @@ -7,8 +7,6 @@ module public import Mathlib.MeasureTheory.Integral.CircleIntegral public import Mathlib.MeasureTheory.Integral.Prod -public import Mathlib.Order.Fin.Tuple -public import Mathlib.Util.Superscript /-! # Integral over a torus in `ℂⁿ` diff --git a/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean b/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean index f092dbf255e704..b6c6a77ec7f4fb 100644 --- a/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean +++ b/Mathlib/MeasureTheory/MeasurableSpace/Basic.lean @@ -152,6 +152,11 @@ theorem comap_map_le : (m.map f).comap f ≤ m := theorem le_map_comap : m ≤ (m.comap g).map g := (gc_comap_map g).le_u_l _ +theorem map_comap_eq_of_surjective (hg : Function.Surjective g) : (m.comap g).map g = m := by + refine le_antisymm (fun S hS => ?_) le_map_comap + rw [map_def, measurableSet_comap] at hS + aesop + end Functors @[simp] theorem map_const {m} (b : β) : MeasurableSpace.map (fun _a : α ↦ b) m = ⊤ := @@ -200,6 +205,10 @@ lemma measurable_comap_iff {mα : MeasurableSpace α} {mγ : MeasurableSpace γ} {f : α → β} {g : β → γ} : Measurable[mα, mγ.comap g] f ↔ Measurable (g ∘ f) := by simp [measurable_iff_comap_le] +lemma measurable_comap_iff_right {mβ : MeasurableSpace β} {mγ : MeasurableSpace γ} {g : α → β} + {f : β → γ} (hg : Function.Surjective g) : Measurable f ↔ Measurable[mβ.comap g] (f ∘ g) := by + rw [measurable_iff_le_map, measurable_iff_le_map, ← map_comp, map_comap_eq_of_surjective hg] + theorem Measurable.mono {ma ma' : MeasurableSpace α} {mb mb' : MeasurableSpace β} {f : α → β} (hf : @Measurable α β ma mb f) (ha : ma ≤ ma') (hb : mb' ≤ mb) : @Measurable α β ma' mb' f := fun _t ht => ha _ <| hf <| hb _ ht diff --git a/Mathlib/MeasureTheory/MeasurableSpace/Constructions.lean b/Mathlib/MeasureTheory/MeasurableSpace/Constructions.lean index fbca0ba2d91c90..05898d572f1af7 100644 --- a/Mathlib/MeasureTheory/MeasurableSpace/Constructions.lean +++ b/Mathlib/MeasureTheory/MeasurableSpace/Constructions.lean @@ -962,6 +962,46 @@ protected lemma Measurable.subset {s t : β → Set α} (hs : Measurable s) (hs end Set +section Finset +variable [MeasurableSpace β] {g : β → Finset α} + +/-- We give `Finset α` the measurable structure inherited from `Set α`. + +This is the smallest sigma-algebra generated by `(a ∈ ·)` for all `a : α`. +See `measurable_finset_iff`. -/ +instance Finset.instMeasurableSpace : MeasurableSpace (Finset α) := + .comap SetLike.coe inferInstance + +lemma measurable_finset_iff_measurable_set : Measurable g ↔ Measurable (fun x ↦ (g x : Set α)) := + measurable_comap_iff + +lemma measurable_finset_iff : Measurable g ↔ ∀ a, Measurable (a ∈ g ·) := by + rw [measurable_finset_iff_measurable_set, measurable_set_iff]; rfl + +lemma measurableSet_finset_iff (S : Set (Finset α)) : MeasurableSet S ↔ + ∃ S' : Set (Set α), MeasurableSet S' ∧ { s : Finset α | ↑s ∈ S'} = S := + MeasurableSpace.measurableSet_comap + +@[fun_prop] +lemma measurable_finset_mem (a : α) : Measurable fun s : Finset α ↦ a ∈ s := + (measurable_set_mem a).comp (comap_measurable _) + +lemma measurable_finset_notMem (a : α) : Measurable fun s : Finset α ↦ a ∉ s := + (measurable_set_notMem a).comp (comap_measurable _) + +lemma measurableSet_mem_finset (a : α) : MeasurableSet {s : Finset α | a ∈ s} := + measurableSet_setOf.2 <| measurable_finset_mem _ + +lemma measurableSet_notMem_finset (a : α) : MeasurableSet {s : Finset α | a ∉ s} := + measurableSet_setOf.2 <| measurable_finset_notMem _ + +variable [Countable α] + +instance Finset.instMeasurableSingletonClass : MeasurableSingletonClass (Finset α) := + .mk fun S ↦ (measurableSet_finset_iff _).mpr ⟨{↑S}, by simp, by ext; simp⟩ + +end Finset + section curry variable {ι : Type*} diff --git a/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean b/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean index d7965599471eca..33747c548193a4 100644 --- a/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean +++ b/Mathlib/MeasureTheory/MeasurableSpace/Defs.lean @@ -6,7 +6,6 @@ Authors: Johannes Hölzl, Mario Carneiro module public import Mathlib.Data.Set.Countable -public import Mathlib.Order.ConditionallyCompleteLattice.Basic public import Mathlib.Tactic.CrossRefAttribute public import Mathlib.Tactic.FunProp.Attr public import Mathlib.Tactic.Measurability diff --git a/Mathlib/MeasureTheory/Measure/CharacteristicFunction/Basic.lean b/Mathlib/MeasureTheory/Measure/CharacteristicFunction/Basic.lean index 82478cadf73698..abf7c13acae298 100644 --- a/Mathlib/MeasureTheory/Measure/CharacteristicFunction/Basic.lean +++ b/Mathlib/MeasureTheory/Measure/CharacteristicFunction/Basic.lean @@ -8,8 +8,6 @@ module public import Mathlib.Analysis.Fourier.BoundedContinuousFunctionChar public import Mathlib.Analysis.Fourier.FourierTransform public import Mathlib.Analysis.InnerProductSpace.Dual -public import Mathlib.Analysis.InnerProductSpace.ProdL2 -public import Mathlib.Analysis.Normed.Lp.MeasurableSpace public import Mathlib.MeasureTheory.Group.IntegralConvolution public import Mathlib.MeasureTheory.Integral.Pi public import Mathlib.MeasureTheory.Measure.FiniteMeasureExt diff --git a/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean b/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean index bfb217a1a6f464..2c5f7e64be0c71 100644 --- a/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean +++ b/Mathlib/MeasureTheory/Measure/CharacteristicFunction/TaylorExpansion.lean @@ -80,8 +80,8 @@ theorem iteratedFDeriv_charFun {n : ℕ} {t : E} (hint : MemLp id n μ) (x : Fin exact contDiff_fourierIntegral _ hint' simp only [mul_inv_rev, neg_smul] rw [h, iteratedFDeriv_fourierIntegral _ hint' (by fun_prop) le_rfl] - simp only [ContinuousMultilinearMap.smul_apply, real_smul, ofReal_pow, ofReal_neg, ofReal_mul, - ofReal_inv, ofReal_ofNat, ofReal_prod] + simp only [smul_apply, real_smul, ofReal_pow, ofReal_neg, ofReal_mul, ofReal_inv, ofReal_ofNat, + ofReal_prod] rw [fourierIntegral_continuousMultilinearMap_apply Real.continuous_fourierChar] swap; · exact integrable_fourierPowSMulRight _ (by simpa using hint.integrable_norm_pow') (by fun_prop) diff --git a/Mathlib/MeasureTheory/Measure/Content.lean b/Mathlib/MeasureTheory/Measure/Content.lean index 54b3e752cbf3cc..284f7f517b31fb 100644 --- a/Mathlib/MeasureTheory/Measure/Content.lean +++ b/Mathlib/MeasureTheory/Measure/Content.lean @@ -5,7 +5,6 @@ Authors: Floris van Doorn -/ module -public import Mathlib.MeasureTheory.Measure.MeasureSpace public import Mathlib.MeasureTheory.Measure.Regular public import Mathlib.Topology.Sets.Compacts diff --git a/Mathlib/MeasureTheory/Measure/Count.lean b/Mathlib/MeasureTheory/Measure/Count.lean index 259adf3b2090ca..da9b3412674521 100644 --- a/Mathlib/MeasureTheory/Measure/Count.lean +++ b/Mathlib/MeasureTheory/Measure/Count.lean @@ -6,7 +6,6 @@ Authors: Johannes Hölzl module public import Mathlib.MeasureTheory.Measure.Dirac -public import Mathlib.Topology.Algebra.InfiniteSum.ENNReal /-! # Counting measure diff --git a/Mathlib/MeasureTheory/Measure/Decomposition/Lebesgue.lean b/Mathlib/MeasureTheory/Measure/Decomposition/Lebesgue.lean index 58230699d62e8d..bc7219a36a8c60 100644 --- a/Mathlib/MeasureTheory/Measure/Decomposition/Lebesgue.lean +++ b/Mathlib/MeasureTheory/Measure/Decomposition/Lebesgue.lean @@ -470,7 +470,7 @@ theorem singularPart_add (μ₁ μ₂ ν : Measure α) [HaveLebesgueDecompositio (μ₁ + μ₂).singularPart ν = μ₁.singularPart ν + μ₂.singularPart ν := by refine (eq_singularPart ((measurable_rnDeriv μ₁ ν).add (measurable_rnDeriv μ₂ ν)) ((mutuallySingular_singularPart _ _).add_left (mutuallySingular_singularPart _ _)) ?_).symm - rw [← Pi.add_def, withDensity_add_left (measurable_rnDeriv μ₁ ν)] + rw [withDensity_add_left (measurable_rnDeriv μ₁ ν)] conv_rhs => rw [add_assoc, add_comm (μ₂.singularPart ν), ← add_assoc, ← add_assoc] rw [← haveLebesgueDecomposition_add μ₁ ν, add_assoc, add_comm (ν.withDensity (μ₂.rnDeriv ν)), ← haveLebesgueDecomposition_add μ₂ ν] diff --git a/Mathlib/MeasureTheory/Measure/Dirac.lean b/Mathlib/MeasureTheory/Measure/Dirac.lean index 6dee190008edeb..c2bcd777e868f6 100644 --- a/Mathlib/MeasureTheory/Measure/Dirac.lean +++ b/Mathlib/MeasureTheory/Measure/Dirac.lean @@ -7,7 +7,7 @@ module public import Mathlib.MeasureTheory.MeasurableSpace.CountablyGenerated public import Mathlib.MeasureTheory.Measure.MutuallySingular -public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms +public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability public import Mathlib.MeasureTheory.Measure.Typeclasses.SFinite @@ -278,7 +278,8 @@ theorem restrict_dirac [MeasurableSingletonClass α] [Decidable (a ∈ s)] : rwa [ae_dirac_eq] · rw [restrict_eq_zero, dirac_apply, indicator_of_notMem has] -lemma mutuallySingular_dirac [MeasurableSingletonClass α] (x : α) (μ : Measure α) [NoAtoms μ] : +lemma mutuallySingular_dirac [MeasurableSingletonClass α] (x : α) (μ : Measure α) + [NullSingletonClass μ] : Measure.dirac x ⟂ₘ μ := ⟨{x}ᶜ, (MeasurableSet.singleton x).compl, by simp, by simp⟩ diff --git a/Mathlib/MeasureTheory/Measure/DiracProba.lean b/Mathlib/MeasureTheory/Measure/DiracProba.lean index a2189b3c2f9972..ff2bc3cf3fd822 100644 --- a/Mathlib/MeasureTheory/Measure/DiracProba.lean +++ b/Mathlib/MeasureTheory/Measure/DiracProba.lean @@ -52,13 +52,11 @@ variable {X : Type*} [MeasurableSpace X] noncomputable def diracProba (x : X) : ProbabilityMeasure X := ⟨Measure.dirac x, Measure.dirac.isProbabilityMeasure⟩ -set_option backward.isDefEq.respectTransparency false in /-- The assignment `x ↦ diracProba x` is injective if all singletons are measurable. -/ lemma injective_diracProba {X : Type*} [MeasurableSpace X] [MeasurableSpace.SeparatesPoints X] : Function.Injective (fun (x : X) ↦ diracProba x) := by intro x y x_eq_y - rw [← dirac_eq_dirac_iff] - rwa [Subtype.ext_iff] at x_eq_y + simpa [diracProba, dirac_eq_dirac_iff] using congr(ProbabilityMeasure.toMeasure $x_eq_y) @[simp] lemma diracProba_toMeasure_apply' (x : X) {A : Set X} (A_mble : MeasurableSet A) : (diracProba x).toMeasure A = A.indicator 1 x := Measure.dirac_apply' x A_mble @@ -98,8 +96,7 @@ lemma not_tendsto_diracProba_of_not_tendsto [CompletelyRegularSpace X] {x : X} ( refine ⟨Ioi 0, Ioi_mem_nhds (by simp only [ENNReal.coe_one, zero_lt_one]), hU.mp (Eventually.of_forall ?_)⟩ intro x x_notin_U - rw [f_vanishes_outside x - (compl_subset_compl.mpr (show interior U ⊆ U from interior_subset) x_notin_U)] + rw [f_vanishes_outside x (compl_subset_compl.mpr interior_subset x_notin_U)] simp only [ENNReal.coe_zero, mem_Ioi, lt_self_iff_false, not_false_eq_true] lemma tendsto_diracProba_iff_tendsto [CompletelyRegularSpace X] {x : X} (L : Filter X) : diff --git a/Mathlib/MeasureTheory/Measure/FiniteMeasure.lean b/Mathlib/MeasureTheory/Measure/FiniteMeasure.lean index ce729bd2c8f65f..76562fe0173c36 100644 --- a/Mathlib/MeasureTheory/Measure/FiniteMeasure.lean +++ b/Mathlib/MeasureTheory/Measure/FiniteMeasure.lean @@ -8,7 +8,6 @@ module public import Mathlib.Analysis.RCLike.Lemmas public import Mathlib.MeasureTheory.Integral.Bochner.ContinuousLinearMap public import Mathlib.MeasureTheory.Measure.HasOuterApproxClosed -public import Mathlib.MeasureTheory.Measure.Prod public import Mathlib.Topology.Algebra.Module.Spaces.WeakDual public import Mathlib.Topology.TietzeExtension diff --git a/Mathlib/MeasureTheory/Measure/FiniteMeasureProd.lean b/Mathlib/MeasureTheory/Measure/FiniteMeasureProd.lean index b579ae7da8e2cf..c80dbc9d83fc47 100644 --- a/Mathlib/MeasureTheory/Measure/FiniteMeasureProd.lean +++ b/Mathlib/MeasureTheory/Measure/FiniteMeasureProd.lean @@ -6,7 +6,6 @@ Authors: Kalle Kytölä module public import Mathlib.MeasureTheory.Measure.LevyProkhorovMetric -public import Mathlib.MeasureTheory.Measure.Prod /-! # Products of finite measures and probability measures diff --git a/Mathlib/MeasureTheory/Measure/Haar/Disintegration.lean b/Mathlib/MeasureTheory/Measure/Haar/Disintegration.lean index cf2c3f61be737f..02e80e2e752add 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/Disintegration.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/Disintegration.lean @@ -5,8 +5,6 @@ Authors: Sébastien Gouëzel -/ module -public import Mathlib.MeasureTheory.Measure.Haar.Basic -public import Mathlib.Analysis.Normed.Module.FiniteDimension public import Mathlib.MeasureTheory.Measure.Haar.Unique /-! @@ -65,8 +63,8 @@ theorem LinearMap.exists_map_addHaar_eq_smul_addHaar' (h : Function.Surjective L let P : S × T →ₗ[𝕜] T := LinearMap.snd 𝕜 S T have P_cont : Continuous P := LinearMap.continuous_of_finiteDimensional _ have I : Function.Bijective (LinearMap.domRestrict L T) := - ⟨LinearMap.injective_domRestrict_iff.2 (IsCompl.inf_eq_bot hT.symm), - (LinearMap.surjective_domRestrict_iff h).2 hT.symm.sup_eq_top⟩ + ⟨LinearMap.injective_domRestrict_iff.2 hT.disjoint.symm, + (LinearMap.surjective_domRestrict_iff h).2 hT.symm.codisjoint⟩ let L' : T ≃ₗ[𝕜] F := LinearEquiv.ofBijective (LinearMap.domRestrict L T) I have L'_cont : Continuous L' := LinearMap.continuous_of_finiteDimensional _ have A : L = (L' : T →ₗ[𝕜] F).comp (P.comp (M.symm : E →ₗ[𝕜] (S × T))) := by diff --git a/Mathlib/MeasureTheory/Measure/Haar/Extension.lean b/Mathlib/MeasureTheory/Measure/Haar/Extension.lean index 5b48903ac5d26f..263f6c3b681fd6 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/Extension.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/Extension.lean @@ -5,7 +5,6 @@ Authors: Thomas Browning -/ module -public import Mathlib.Analysis.InnerProductSpace.Basic public import Mathlib.MeasureTheory.Group.Integral public import Mathlib.MeasureTheory.Integral.RieszMarkovKakutani.Real public import Mathlib.Topology.Algebra.Group.Extension diff --git a/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean b/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean index ded8c0bfb1516a..6fdb13e36dee52 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/InnerProductSpace.lean @@ -192,7 +192,7 @@ theorem WithLp.volume_preserving_symm_measurableEquiv_toLp_prod : convert! this ext uv <;> simp [volumePreservingSymmMeasurableEquivToLpProdAux, MeasurableEquiv.coe_sumPiEquivProdPi, - LinearEquiv.prodCongr_symm, MeasurableEquiv.prodCongr] + MeasurableEquiv.prodCongr] refine (LinearIsometryEquiv.measurePreserving _).trans ?_ refine (EuclideanSpace.volume_preserving_symm_measurableEquiv_toLp _).trans ?_ refine (measurePreserving_sumPiEquivProdPi _).trans ?_ diff --git a/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean b/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean index a22ac24d3625df..d50348a103a29f 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/NormedSpace.lean @@ -6,7 +6,6 @@ Authors: Floris van Doorn, Sébastien Gouëzel module public import Mathlib.MeasureTheory.Measure.Haar.InnerProductSpace -public import Mathlib.MeasureTheory.Measure.Lebesgue.EqHaar public import Mathlib.MeasureTheory.Integral.Bochner.Set /-! @@ -25,10 +24,12 @@ namespace MeasureTheory namespace Measure -/-- The instance `MeasureTheory.Measure.IsAddHaarMeasure.noAtoms` applies in particular to show that -an additive Haar measure on a nontrivial finite-dimensional real vector space has no atom. -/ +/-- The instance `MeasureTheory.Measure.IsAddHaarMeasure.nullSingletonClass` applies in particular +to show that an additive Haar measure on a nontrivial finite-dimensional real vector space has no +atom. -/ example {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] [Nontrivial E] [FiniteDimensional ℝ E] - [MeasurableSpace E] [BorelSpace E] (μ : Measure E) [IsAddHaarMeasure μ] : NoAtoms μ := by + [MeasurableSpace E] [BorelSpace E] (μ : Measure E) [IsAddHaarMeasure μ] : + NullSingletonClass μ := by infer_instance section LinearEquiv diff --git a/Mathlib/MeasureTheory/Measure/Haar/Quotient.lean b/Mathlib/MeasureTheory/Measure/Haar/Quotient.lean index 3d3fd8c253abcf..568e8541bf3d58 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/Quotient.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/Quotient.lean @@ -5,11 +5,8 @@ Authors: Alex Kontorovich, Heather Macbeth -/ module -public import Mathlib.Algebra.Group.Opposite -public import Mathlib.MeasureTheory.Constructions.Polish.Basic public import Mathlib.MeasureTheory.Group.FundamentalDomain public import Mathlib.MeasureTheory.Integral.DominatedConvergence -public import Mathlib.MeasureTheory.Measure.Haar.Basic /-! # Haar quotient measure diff --git a/Mathlib/MeasureTheory/Measure/Haar/Unique.lean b/Mathlib/MeasureTheory/Measure/Haar/Unique.lean index 218a060a1bf085..dd3a0f69aaeb57 100644 --- a/Mathlib/MeasureTheory/Measure/Haar/Unique.lean +++ b/Mathlib/MeasureTheory/Measure/Haar/Unique.lean @@ -5,12 +5,9 @@ Authors: Sébastien Gouëzel -/ module -public import Mathlib.MeasureTheory.Function.LocallyIntegrable public import Mathlib.MeasureTheory.Group.Integral public import Mathlib.MeasureTheory.Integral.Prod -public import Mathlib.MeasureTheory.Integral.Bochner.Set public import Mathlib.MeasureTheory.Measure.EverywherePos -public import Mathlib.MeasureTheory.Measure.Haar.Basic public import Mathlib.Topology.Metrizable.Urysohn public import Mathlib.Topology.ContinuousMap.Ordered diff --git a/Mathlib/MeasureTheory/Measure/HasOuterApproxClosed.lean b/Mathlib/MeasureTheory/Measure/HasOuterApproxClosed.lean index 0f8e45df09884c..82b1e677a1f03b 100644 --- a/Mathlib/MeasureTheory/Measure/HasOuterApproxClosed.lean +++ b/Mathlib/MeasureTheory/Measure/HasOuterApproxClosed.lean @@ -6,6 +6,7 @@ Authors: Kalle Kytölä module public import Mathlib.MeasureTheory.Integral.BoundedContinuousFunction +public import Mathlib.MeasureTheory.Integral.IntegrableOn public import Mathlib.Topology.MetricSpace.ThickenedIndicator /-! diff --git a/Mathlib/MeasureTheory/Measure/Hausdorff.lean b/Mathlib/MeasureTheory/Measure/Hausdorff.lean index 4377338e2a1943..8a42736fa80c53 100644 --- a/Mathlib/MeasureTheory/Measure/Hausdorff.lean +++ b/Mathlib/MeasureTheory/Measure/Hausdorff.lean @@ -6,7 +6,6 @@ Authors: Yury Kudryashov module public import Mathlib.Analysis.Convex.Between -public import Mathlib.MeasureTheory.Constructions.BorelSpace.Basic public import Mathlib.MeasureTheory.Measure.Lebesgue.Basic public import Mathlib.Topology.MetricSpace.Holder public import Mathlib.Topology.MetricSpace.MetricSeparated @@ -80,7 +79,8 @@ measures. equal to infinity on some ray `(-∞, D)` and is equal to zero on `(D, +∞)`, where `D` is a possibly infinite number called the *Hausdorff dimension* of `s`; `μH[D] s` can be zero, infinity, or anything in between. -* `MeasureTheory.Measure.noAtoms_hausdorff`: Hausdorff measure has no atoms. +* `MeasureTheory.Measure.nullSingletonClass_hausdorff`: Hausdorff measure has value zero on + singletons. ### Hausdorff measure in `ℝⁿ` @@ -343,9 +343,9 @@ theorem mkMetric_top : (mkMetric (fun _ => ∞ : ℝ≥0∞ → ℝ≥0∞) : Ou intro b hb simpa using hb ⊤ -/-- If `m₁ d ≤ m₂ d` for `d < ε` for some `ε > 0` (we use `≤ᶠ[𝓝[≥] 0]` to state this), then +/-- If `m₁ d ≤ m₂ d` for `d < ε` for some `ε > 0` (we use `≤ᶠ[𝓝 0]` to state this), then `mkMetric m₁ hm₁ ≤ mkMetric m₂ hm₂`. -/ -theorem mkMetric_mono {m₁ m₂ : ℝ≥0∞ → ℝ≥0∞} (hle : m₁ ≤ᶠ[𝓝[≥] 0] m₂) : +theorem mkMetric_mono {m₁ m₂ : ℝ≥0∞ → ℝ≥0∞} (hle : m₁ ≤ᶠ[𝓝 0] m₂) : (mkMetric m₁ : OuterMeasure X) ≤ mkMetric m₂ := by convert! @mkMetric_mono_smul X _ _ m₂ _ ENNReal.one_ne_top one_ne_zero _ <;> simp [*] @@ -455,9 +455,9 @@ theorem mkMetric_top : (mkMetric (fun _ => ∞ : ℝ≥0∞ → ℝ≥0∞) : Me apply toOuterMeasure_injective rw [mkMetric_toOuterMeasure, OuterMeasure.mkMetric_top, toOuterMeasure_top] -/-- If `m₁ d ≤ m₂ d` for `d < ε` for some `ε > 0` (we use `≤ᶠ[𝓝[≥] 0]` to state this), then +/-- If `m₁ d ≤ m₂ d` for `d < ε` for some `ε > 0` (we use `≤ᶠ[𝓝 0]` to state this), then `mkMetric m₁ hm₁ ≤ mkMetric m₂ hm₂`. -/ -theorem mkMetric_mono {m₁ m₂ : ℝ≥0∞ → ℝ≥0∞} (hle : m₁ ≤ᶠ[𝓝[≥] 0] m₂) : +theorem mkMetric_mono {m₁ m₂ : ℝ≥0∞ → ℝ≥0∞} (hle : m₁ ≤ᶠ[𝓝 0] m₂) : (mkMetric m₁ : Measure X) ≤ mkMetric m₂ := by convert! @mkMetric_mono_smul X _ _ _ _ m₂ _ ENNReal.one_ne_top one_ne_zero _ <;> simp [*] @@ -601,7 +601,8 @@ theorem hausdorffMeasure_mono {d₁ d₂ : ℝ} (h : d₁ ≤ d₂) (s : Set X) rcases hausdorffMeasure_zero_or_top h s with hs | hs <;> simp [hs] variable (X) in -theorem noAtoms_hausdorff {d : ℝ} (hd : 0 < d) : NoAtoms (hausdorffMeasure d : Measure X) := by +theorem nullSingletonClass_hausdorff {d : ℝ} (hd : 0 < d) : + NullSingletonClass (hausdorffMeasure d : Measure X) := by refine ⟨fun x => ?_⟩ rw [← nonpos_iff_eq_zero, hausdorffMeasure_apply] refine iSup₂_le fun ε _ => iInf₂_le_of_le (fun _ => {x}) ?_ <| iInf_le_of_le (fun _ => ?_) ?_ @@ -609,6 +610,9 @@ theorem noAtoms_hausdorff {d : ℝ} (hd : 0 < d) : NoAtoms (hausdorffMeasure d : · simp only [ediam_singleton, zero_le] · simp [hd] +@[deprecated (since := "2026-06-09")] +alias noAtoms_hausdorff := nullSingletonClass_hausdorff + @[simp] theorem hausdorffMeasure_zero_singleton (x : X) : μH[0] ({x} : Set X) = 1 := by apply le_antisymm @@ -647,7 +651,7 @@ theorem hausdorffMeasure_le_one_of_subsingleton {s : Set X} (hs : s.Subsingleton · rw [(subsingleton_iff_singleton hx).1 hs] rcases eq_or_lt_of_le hd with (rfl | dpos) · simp only [le_refl, hausdorffMeasure_zero_singleton] - · haveI := noAtoms_hausdorff X dpos + · haveI := nullSingletonClass_hausdorff X dpos simp only [zero_le, measure_singleton] end Measure @@ -685,7 +689,7 @@ theorem hausdorffMeasure_image_le (h : HolderOnWith C r f s) (hr : 0 < r) {d : · simp only [ENNReal.rpow_zero, one_mul, mul_zero] rw [hausdorffMeasure_zero_singleton] exact one_le_hausdorffMeasure_zero_of_nonempty ⟨x, hx⟩ - · haveI := noAtoms_hausdorff Y h'd + · haveI := nullSingletonClass_hausdorff Y h'd simp only [zero_le, measure_singleton] -- Now assume `C ≠ 0` · have hCd0 : (C : ℝ≥0∞) ^ d ≠ 0 := by simp [hC0.ne'] @@ -775,7 +779,7 @@ theorem hausdorffMeasure_preimage_le (hf : AntilipschitzWith K f) (hd : 0 ≤ d) · simp only [ENNReal.rpow_zero, one_mul] rw [hausdorffMeasure_zero_singleton] exact one_le_hausdorffMeasure_zero_of_nonempty ⟨f x, hx⟩ - · haveI := noAtoms_hausdorff X h'd + · haveI := nullSingletonClass_hausdorff X h'd simp only [zero_le, measure_singleton] have hKd0 : (K : ℝ≥0∞) ^ d ≠ 0 := by simp [h0] have hKd : (K : ℝ≥0∞) ^ d ≠ ∞ := by simp [hd] @@ -1023,7 +1027,7 @@ theorem hausdorffMeasure_smul_right_image [NormedAddCommGroup E] [NormedSpace [MeasurableSpace E] [BorelSpace E] (v : E) (s : Set ℝ) : μH[1] ((fun r => r • v) '' s) = ‖v‖₊ • μH[1] s := by obtain rfl | hv := eq_or_ne v 0 - · haveI := noAtoms_hausdorff E one_pos + · haveI := nullSingletonClass_hausdorff E one_pos obtain rfl | hs := s.eq_empty_or_nonempty · simp simp [hs] diff --git a/Mathlib/MeasureTheory/Measure/IntegralCharFun.lean b/Mathlib/MeasureTheory/Measure/IntegralCharFun.lean index 3bd1c948d35cb9..cae25b2ff94c6a 100644 --- a/Mathlib/MeasureTheory/Measure/IntegralCharFun.lean +++ b/Mathlib/MeasureTheory/Measure/IntegralCharFun.lean @@ -5,7 +5,6 @@ Authors: Rémy Degenne -/ module -public import Mathlib.Analysis.SpecialFunctions.Integrals.Basic public import Mathlib.MeasureTheory.Function.SpecialFunctions.Sinc public import Mathlib.MeasureTheory.Measure.CharacteristicFunction.Basic diff --git a/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean b/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean index a72b18d4548e7a..817ddd25d63ce9 100644 --- a/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean +++ b/Mathlib/MeasureTheory/Measure/Lebesgue/Basic.lean @@ -5,10 +5,7 @@ Authors: Johannes Hölzl, Sébastien Gouëzel, Yury Kudryashov -/ module -public import Mathlib.Dynamics.Ergodic.MeasurePreserving -public import Mathlib.LinearAlgebra.Determinant public import Mathlib.LinearAlgebra.Matrix.Diagonal -public import Mathlib.LinearAlgebra.Matrix.Transvection public import Mathlib.MeasureTheory.Group.LIntegral public import Mathlib.MeasureTheory.Integral.Marginal public import Mathlib.MeasureTheory.Measure.Stieltjes @@ -170,9 +167,12 @@ theorem volume_closedEBall (a : ℝ) (r : ℝ≥0∞) : volume (Metric.closedEBa @[deprecated (since := "2026-01-24")] alias volume_emetric_closedBall := volume_closedEBall -instance noAtoms_volume : NoAtoms (volume : Measure ℝ) := +instance nullSingletonClass_volume : NullSingletonClass (volume : Measure ℝ) := ⟨fun _ => volume_singleton⟩ +@[deprecated (since := "2026-06-09")] +alias noAtoms_volume := nullSingletonClass_volume + @[simp] theorem volume_interval {a b : ℝ} : volume (uIcc a b) = ofReal |b - a| := by rw [← Icc_min_max, volume_Icc, max_sub_min_eq_abs] @@ -619,8 +619,8 @@ end regionBetween /-- Consider a real set `s`. If a property is true almost everywhere in `s ∩ (a, b)` for all `a, b ∈ s`, then it is true almost everywhere in `s`. Formulated with `μ.restrict`. See also `ae_of_mem_of_ae_of_mem_inter_Ioo`. -/ -theorem ae_restrict_of_ae_restrict_inter_Ioo {μ : Measure ℝ} [NoAtoms μ] {s : Set ℝ} {p : ℝ → Prop} - (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ.restrict (s ∩ Ioo a b), p x) : +theorem ae_restrict_of_ae_restrict_inter_Ioo {μ : Measure ℝ} [NullSingletonClass μ] {s : Set ℝ} + {p : ℝ → Prop} (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ.restrict (s ∩ Ioo a b), p x) : ∀ᵐ x ∂μ.restrict s, p x := by /- By second-countability, we cover `s` by countably many intervals `(a, b)` (except maybe for two endpoints, which don't matter since `μ` does not have any atom). -/ @@ -653,8 +653,8 @@ theorem ae_restrict_of_ae_restrict_inter_Ioo {μ : Measure ℝ} [NoAtoms μ] {s /-- Consider a real set `s`. If a property is true almost everywhere in `s ∩ (a, b)` for all `a, b ∈ s`, then it is true almost everywhere in `s`. Formulated with bare membership. See also `ae_restrict_of_ae_restrict_inter_Ioo`. -/ -theorem ae_of_mem_of_ae_of_mem_inter_Ioo {μ : Measure ℝ} [NoAtoms μ] {s : Set ℝ} {p : ℝ → Prop} - (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ, x ∈ s ∩ Ioo a b → p x) : +theorem ae_of_mem_of_ae_of_mem_inter_Ioo {μ : Measure ℝ} [NullSingletonClass μ] {s : Set ℝ} + {p : ℝ → Prop} (h : ∀ a b, a ∈ s → b ∈ s → a < b → ∀ᵐ x ∂μ, x ∈ s ∩ Ioo a b → p x) : ∀ᵐ x ∂μ, x ∈ s → p x := by /- By second-countability, we cover `s` by countably many intervals `(a, b)` (except maybe for two endpoints, which don't matter since `μ` does not have any atom). -/ diff --git a/Mathlib/MeasureTheory/Measure/Lebesgue/EqHaar.lean b/Mathlib/MeasureTheory/Measure/Lebesgue/EqHaar.lean index 3de4107fcc30b7..70624b7e155506 100644 --- a/Mathlib/MeasureTheory/Measure/Lebesgue/EqHaar.lean +++ b/Mathlib/MeasureTheory/Measure/Lebesgue/EqHaar.lean @@ -5,11 +5,6 @@ Authors: Floris van Doorn, Sébastien Gouëzel -/ module -public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas -public import Mathlib.MeasureTheory.Constructions.BorelSpace.Metric -public import Mathlib.MeasureTheory.Group.Pointwise -public import Mathlib.MeasureTheory.Measure.Doubling -public import Mathlib.MeasureTheory.Measure.Haar.Basic public import Mathlib.MeasureTheory.Measure.Lebesgue.Basic /-! diff --git a/Mathlib/MeasureTheory/Measure/Lebesgue/Integral.lean b/Mathlib/MeasureTheory/Measure/Lebesgue/Integral.lean index 40cd194ef17765..02ea8b19c7aa47 100644 --- a/Mathlib/MeasureTheory/Measure/Lebesgue/Integral.lean +++ b/Mathlib/MeasureTheory/Measure/Lebesgue/Integral.lean @@ -5,8 +5,6 @@ Authors: Johannes Hölzl, Sébastien Gouëzel, Yury Kudryashov -/ module -public import Mathlib.MeasureTheory.Integral.Bochner.Set -public import Mathlib.MeasureTheory.Measure.Lebesgue.Basic public import Mathlib.MeasureTheory.Measure.Haar.Unique /-! # Properties of integration with respect to the Lebesgue measure -/ diff --git a/Mathlib/MeasureTheory/Measure/LevyProkhorovMetric.lean b/Mathlib/MeasureTheory/Measure/LevyProkhorovMetric.lean index 2cb94134114021..c33e6c53d47498 100644 --- a/Mathlib/MeasureTheory/Measure/LevyProkhorovMetric.lean +++ b/Mathlib/MeasureTheory/Measure/LevyProkhorovMetric.lean @@ -8,7 +8,6 @@ module public import Mathlib.MeasureTheory.Measure.Portmanteau public import Mathlib.MeasureTheory.Integral.DominatedConvergence public import Mathlib.MeasureTheory.Integral.Layercake -public import Mathlib.MeasureTheory.Integral.BoundedContinuousFunction /-! # The Lévy-Prokhorov distance on spaces of finite measures and probability measures diff --git a/Mathlib/MeasureTheory/Measure/Map.lean b/Mathlib/MeasureTheory/Measure/Map.lean index 71761db206640f..fa9fb8055d9343 100644 --- a/Mathlib/MeasureTheory/Measure/Map.lean +++ b/Mathlib/MeasureTheory/Measure/Map.lean @@ -49,10 +49,10 @@ def liftLinear [MeasurableSpace β] (f : OuterMeasure α →ₗ[ℝ≥0∞] Oute toFun μ := (f μ.toOuterMeasure).toMeasure (hf μ) map_add' μ₁ μ₂ := ext fun s hs => by simp only [map_add, coe_add, Pi.add_apply, toMeasure_apply, add_toOuterMeasure, - OuterMeasure.coe_add, hs] + FunLike.coe_add, hs] map_smul' c μ := ext fun s hs => by simp only [map_smulₛₗ, Pi.smul_apply, toMeasure_apply, smul_toOuterMeasure (R := ℝ≥0∞), - OuterMeasure.coe_smul (R := ℝ≥0∞), smul_apply, hs] + FunLike.coe_smul, smul_apply, hs] lemma liftLinear_apply₀ {f : OuterMeasure α →ₗ[ℝ≥0∞] OuterMeasure β} (hf) {s : Set β} (hs : NullMeasurableSet s (liftLinear f hf μ)) : liftLinear f hf μ s = f μ.toOuterMeasure s := diff --git a/Mathlib/MeasureTheory/Measure/MeasureSpace.lean b/Mathlib/MeasureTheory/Measure/MeasureSpace.lean index b3f45e6a32eb42..4f1e08faed2b5d 100644 --- a/Mathlib/MeasureTheory/Measure/MeasureSpace.lean +++ b/Mathlib/MeasureTheory/Measure/MeasureSpace.lean @@ -929,7 +929,7 @@ instance instSMul {_ : MeasurableSpace α} : SMul R (Measure α) := ⟨fun c μ => { toOuterMeasure := c • μ.toOuterMeasure m_iUnion := fun s hs hd => by - simp only [OuterMeasure.smul_apply, coe_toOuterMeasure, ENNReal.tsum_const_smul, + simp only [smul_apply, coe_toOuterMeasure, ENNReal.tsum_const_smul, measure_iUnion hd hs] trim_le := by rw [OuterMeasure.trim_smul, μ.trimmed] }⟩ diff --git a/Mathlib/MeasureTheory/Measure/NullMeasurable.lean b/Mathlib/MeasureTheory/Measure/NullMeasurable.lean index 3f645566c47cc3..32f7c49a57ec86 100644 --- a/Mathlib/MeasureTheory/Measure/NullMeasurable.lean +++ b/Mathlib/MeasureTheory/Measure/NullMeasurable.lean @@ -6,7 +6,6 @@ Authors: Johannes Hölzl, Mario Carneiro, Yury Kudryashov module public import Mathlib.MeasureTheory.MeasurableSpace.EventuallyMeasurable -public import Mathlib.MeasureTheory.MeasurableSpace.Basic public import Mathlib.MeasureTheory.Measure.AEDisjoint /-! diff --git a/Mathlib/MeasureTheory/Measure/OpenPos.lean b/Mathlib/MeasureTheory/Measure/OpenPos.lean index d5e7375f730923..46c9fb552b916b 100644 --- a/Mathlib/MeasureTheory/Measure/OpenPos.lean +++ b/Mathlib/MeasureTheory/Measure/OpenPos.lean @@ -6,7 +6,7 @@ Authors: Yury Kudryashov module public import Mathlib.MeasureTheory.Constructions.BorelSpace.Basic -public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms +public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability /-! @@ -217,7 +217,7 @@ theorem measure_closedBall_pos (x : X) {r : ℝ} (hr : 0 < r) : 0 < μ (closedBa (measure_ball_pos μ x hr).trans_le (measure_mono ball_subset_closedBall) @[simp] lemma measure_closedBall_pos_iff {X : Type*} [MetricSpace X] {m : MeasurableSpace X} - (μ : Measure X) [IsOpenPosMeasure μ] [NoAtoms μ] {x : X} {r : ℝ} : + (μ : Measure X) [IsOpenPosMeasure μ] [NullSingletonClass μ] {x : X} {r : ℝ} : 0 < μ (closedBall x r) ↔ 0 < r := by refine ⟨fun h ↦ ?_, measure_closedBall_pos μ x⟩ contrapose! h diff --git a/Mathlib/MeasureTheory/Measure/PreVariation.lean b/Mathlib/MeasureTheory/Measure/PreVariation.lean index d0cd3fbe68d460..b407ad6c372e07 100644 --- a/Mathlib/MeasureTheory/Measure/PreVariation.lean +++ b/Mathlib/MeasureTheory/Measure/PreVariation.lean @@ -55,6 +55,15 @@ noncomputable def preVariationFun (s : Set X) : ℝ≥0∞ := ⨆ (P : Finpartition (⟨s, h⟩ : Subtype MeasurableSet)), ∑ p ∈ P.parts, f p else 0 +lemma preVariationFun_apply {s : Set X} (h : MeasurableSet s) : + preVariationFun f s = + ⨆ (P : Finpartition (⟨s, h⟩ : Subtype MeasurableSet)), ∑ p ∈ P.parts, f p := by + simp [preVariationFun, h] + +lemma preVariationFun_of_not_measurableSet {s : Set X} (h : ¬ MeasurableSet s) : + preVariationFun f s = 0 := by + simp [preVariationFun, h] + end namespace preVariation diff --git a/Mathlib/MeasureTheory/Measure/ProbabilityMeasure.lean b/Mathlib/MeasureTheory/Measure/ProbabilityMeasure.lean index 9b226f23179f49..6546d9f12b53b9 100644 --- a/Mathlib/MeasureTheory/Measure/ProbabilityMeasure.lean +++ b/Mathlib/MeasureTheory/Measure/ProbabilityMeasure.lean @@ -7,7 +7,6 @@ module public import Mathlib.MeasureTheory.Measure.FiniteMeasure public import Mathlib.MeasureTheory.Integral.Average -public import Mathlib.MeasureTheory.Measure.Prod /-! # Probability measures diff --git a/Mathlib/MeasureTheory/Measure/Prod.lean b/Mathlib/MeasureTheory/Measure/Prod.lean index e9f92b71955eb6..2f3c5ca3e1db60 100644 --- a/Mathlib/MeasureTheory/Measure/Prod.lean +++ b/Mathlib/MeasureTheory/Measure/Prod.lean @@ -202,16 +202,16 @@ theorem prod_prod_le (s : Set α) (t : Set β) : μ.prod ν (s ×ˢ t) ≤ μ s restrict_apply_univ, mul_comm] _ = μ s * ν t := by rw [measure_toMeasurable, measure_toMeasurable] -instance prod.instNoAtoms_fst [NoAtoms μ] : - NoAtoms (Measure.prod μ ν) where +instance prod.instNullSingletonClass_fst [NullSingletonClass μ] : + NullSingletonClass (Measure.prod μ ν) where measure_singleton | (x, y) => nonpos_iff_eq_zero.mp <| calc μ.prod ν {(x, y)} = μ.prod ν ({x} ×ˢ {y}) := by rw [singleton_prod_singleton] _ ≤ μ {x} * ν {y} := prod_prod_le _ _ _ = 0 := by simp -instance prod.instNoAtoms_snd [NoAtoms ν] : - NoAtoms (Measure.prod μ ν) where +instance prod.instNullSingletonClass_snd [NullSingletonClass ν] : + NullSingletonClass (Measure.prod μ ν) where measure_singleton | (x, y) => nonpos_iff_eq_zero.mp <| calc μ.prod ν {(x, y)} = μ.prod ν ({x} ×ˢ {y}) := by rw [singleton_prod_singleton] @@ -419,6 +419,14 @@ theorem AbsolutelyContinuous.prod [SFinite ν'] (h1 : μ ≪ μ') (h2 : ν ≪ rw [measure_prod_null hs] at h2s exact (h2s.filter_mono h1.ae_le).mono fun _ h => h2 h +omit [SFinite ν] in +@[gcongr] theorem prod_mono [SFinite ν'] (h1 : μ ≤ μ') (h2 : ν ≤ ν') : μ.prod ν ≤ μ'.prod ν' := by + apply Measure.le_iff.2 (fun s hs ↦ ?_) + calc μ.prod ν s + _ ≤ ∫⁻ x, ν (Prod.mk x ⁻¹' s) ∂μ := prod_apply_le hs + _ ≤ ∫⁻ x, ν' (Prod.mk x ⁻¹' s) ∂μ' := by gcongr + _ = (μ'.prod ν') s := (prod_apply hs).symm + /-- Note: the converse is not true. For a counterexample, see Walter Rudin *Real and Complex Analysis*, example (c) in section 8.9. It is true if the set is measurable, see `ae_prod_mem_iff_ae_ae_mem`. -/ @@ -835,7 +843,8 @@ theorem map_prod_map {δ} [MeasurableSpace δ] {f : α → β} {g : γ → δ} ( -- `prod_smul_right` needs an instance to get `SFinite (c • ν)` from `SFinite ν`, -- hence it is placed in the `WithDensity` file, where the instance is defined. -lemma prod_smul_left {μ : Measure α} (c : ℝ≥0∞) : (c • μ).prod ν = c • (μ.prod ν) := by +lemma prod_smul_left {μ : Measure α} {R : Type*} [SMul R ℝ≥0∞] [IsScalarTower R ℝ≥0∞ ℝ≥0∞] + (c : R) : (c • μ).prod ν = c • (μ.prod ν) := by ext s hs rw [prod_apply hs, Measure.smul_apply, prod_apply hs] simp diff --git a/Mathlib/MeasureTheory/Measure/Real.lean b/Mathlib/MeasureTheory/Measure/Real.lean index 0d15c02f097b66..ec1f88b8aa8fe4 100644 --- a/Mathlib/MeasureTheory/Measure/Real.lean +++ b/Mathlib/MeasureTheory/Measure/Real.lean @@ -493,9 +493,9 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: applications of `μ.real` are nonnegative. -/ @[positivity MeasureTheory.Measure.real _ _] -meta def evalMeasureReal : PositivityExt where eval {_ _} _zα pα? e := do +meta def evalMeasureReal : PositivityExt where eval {_ _} _zα pα? e := + match pα? with | none => pure .none | some _ => do let .app (.app _ a) b ← whnfR e | throwError "not measureReal" - let some _ := pα? | pure .none let p ← mkAppOptM ``MeasureTheory.measureReal_nonneg #[none, none, a, b] pure (.nonnegative p) diff --git a/Mathlib/MeasureTheory/Measure/RegularityCompacts.lean b/Mathlib/MeasureTheory/Measure/RegularityCompacts.lean index 876922d32cf586..31eda309cefffb 100644 --- a/Mathlib/MeasureTheory/Measure/RegularityCompacts.lean +++ b/Mathlib/MeasureTheory/Measure/RegularityCompacts.lean @@ -5,11 +5,8 @@ Authors: Rémy Degenne, Peter Pfaffelhuber -/ module -public import Mathlib.Analysis.SpecificLimits.Basic public import Mathlib.MeasureTheory.Measure.Regular -public import Mathlib.Topology.GDelta.MetrizableSpace public import Mathlib.Topology.MetricSpace.Polish -public import Mathlib.Topology.UniformSpace.Cauchy /-! # Inner regularity of finite measures diff --git a/Mathlib/MeasureTheory/Measure/Restrict.lean b/Mathlib/MeasureTheory/Measure/Restrict.lean index 7d145ac05dc563..8c50c7c32662ea 100644 --- a/Mathlib/MeasureTheory/Measure/Restrict.lean +++ b/Mathlib/MeasureTheory/Measure/Restrict.lean @@ -6,8 +6,6 @@ Authors: Johannes Hölzl, Mario Carneiro module public import Mathlib.MeasureTheory.Measure.Comap -public import Mathlib.MeasureTheory.Measure.QuasiMeasurePreserving -public import Mathlib.Data.Set.Card /-! # Restricting a measure to a subset or a subtype diff --git a/Mathlib/MeasureTheory/Measure/Stieltjes.lean b/Mathlib/MeasureTheory/Measure/Stieltjes.lean index f61f16fa6b1d78..72ca1eecf4560c 100644 --- a/Mathlib/MeasureTheory/Measure/Stieltjes.lean +++ b/Mathlib/MeasureTheory/Measure/Stieltjes.lean @@ -7,7 +7,6 @@ module public import Mathlib.MeasureTheory.Constructions.BorelSpace.Order public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability -public import Mathlib.Topology.Algebra.UniformMulAction public import Mathlib.Topology.Order.LeftRightLim /-! @@ -148,8 +147,6 @@ theorem iInf_Ioi_eq [OrderTopology R] [DenselyOrdered R] [NoMaxOrder R] (f : StieltjesFunction R) (x : R) : ⨅ r : Ioi x, f r = f x := by suffices Function.rightLim f x = ⨅ r : Ioi x, f r by rw [← this, f.rightLim_eq] rw [f.mono.rightLim_eq_sInf, sInf_image'] - rw [← neBot_iff] - infer_instance theorem iInf_rat_gt_eq (f : StieltjesFunction ℝ) (x : ℝ) : ⨅ r : { r' : ℚ // x < r' }, f r = f x := by diff --git a/Mathlib/MeasureTheory/Measure/SubFinite.lean b/Mathlib/MeasureTheory/Measure/SubFinite.lean index e520f646bfceb3..6e0c40c30c160e 100644 --- a/Mathlib/MeasureTheory/Measure/SubFinite.lean +++ b/Mathlib/MeasureTheory/Measure/SubFinite.lean @@ -8,7 +8,6 @@ module public import Mathlib.MeasureTheory.Measure.Sub import Mathlib.MeasureTheory.Integral.Lebesgue.Sub -public import Mathlib.Analysis.Normed.Group.Basic public import Mathlib.MeasureTheory.Measure.Decomposition.Hahn public import Mathlib.MeasureTheory.Measure.WithDensity diff --git a/Mathlib/MeasureTheory/Measure/Trim.lean b/Mathlib/MeasureTheory/Measure/Trim.lean index c5484091922868..8d0dab87fee50e 100644 --- a/Mathlib/MeasureTheory/Measure/Trim.lean +++ b/Mathlib/MeasureTheory/Measure/Trim.lean @@ -112,6 +112,15 @@ theorem restrict_trim (hm : m ≤ m0) (μ : Measure α) (hs : @MeasurableSet α Measure.restrict_apply (hm t ht), trim_measurableSet_eq hm (@MeasurableSet.inter α m t s ht hs)] +theorem measure_spanningSets_trim_lt_top (hm : m ≤ m0) (μ : Measure α) [SigmaFinite (μ.trim hm)] + (n : ℕ) : + μ (spanningSets (μ.trim hm) n) < ⊤ := + (le_trim hm).trans_lt (measure_spanningSets_lt_top (μ.trim hm) n) + +instance (hm : m ≤ m0) (μ : Measure α) [SigmaFinite (μ.trim hm)] (n : ℕ) : + IsFiniteMeasure (μ.restrict (spanningSets (μ.trim hm) n)) := + isFiniteMeasure_restrict.2 (measure_spanningSets_trim_lt_top hm μ n).ne + instance isFiniteMeasure_trim (hm : m ≤ m0) [IsFiniteMeasure μ] : IsFiniteMeasure (μ.trim hm) where measure_univ_lt_top := by rw [trim_measurableSet_eq hm (@MeasurableSet.univ _ m)] @@ -119,19 +128,12 @@ instance isFiniteMeasure_trim (hm : m ≤ m0) [IsFiniteMeasure μ] : IsFiniteMea theorem sigmaFiniteTrim_mono {m m₂ m0 : MeasurableSpace α} {μ : Measure α} (hm : m ≤ m0) (hm₂ : m₂ ≤ m) [SigmaFinite (μ.trim (hm₂.trans hm))] : SigmaFinite (μ.trim hm) := by - refine ⟨⟨?_⟩⟩ - refine - { set := spanningSets (μ.trim (hm₂.trans hm)) + have : SigmaFinite ((μ.trim hm).trim hm₂) := by simpa [trim_trim] + exact ⟨⟨ + { set := spanningSets ((μ.trim hm).trim hm₂) set_mem := fun _ => Set.mem_univ _ - finite := fun i => ?_ - spanning := iUnion_spanningSets _ } - calc - (μ.trim hm) (spanningSets (μ.trim (hm₂.trans hm)) i) = - ((μ.trim hm).trim hm₂) (spanningSets (μ.trim (hm₂.trans hm)) i) := by - rw [@trim_measurableSet_eq α m₂ m (μ.trim hm) _ hm₂ (measurableSet_spanningSets _ _)] - _ = (μ.trim (hm₂.trans hm)) (spanningSets (μ.trim (hm₂.trans hm)) i) := by - rw [@trim_trim _ _ μ _ _ hm₂ hm] - _ < ∞ := measure_spanningSets_lt_top _ _ + finite := fun i => measure_spanningSets_trim_lt_top hm₂ (μ.trim hm) i + spanning := iUnion_spanningSets _ }⟩⟩ lemma SigmaFinite.of_trim {m m0 : MeasurableSpace α} {μ : Measure α} (hm : m ≤ m0) [SigmaFinite (μ.trim hm)] : SigmaFinite μ := by diff --git a/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean b/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean index c2688f3a1427d9..10b58e0c999fe1 100644 --- a/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean +++ b/Mathlib/MeasureTheory/Measure/Typeclasses/NoAtoms.lean @@ -1,156 +1,9 @@ -/- -Copyright (c) 2017 Johannes Hölzl. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Johannes Hölzl, Mario Carneiro --/ module -public import Mathlib.MeasureTheory.Measure.Restrict -public import Mathlib.Topology.DiscreteSubset - -/-! -# Measures having no atoms - -A measure `μ` has no atoms if the measure of each singleton is zero. - -## TODO - -Should `NoAtoms` be redefined as `∀ s, 0 < μ s → ∃ t ⊆ s, 0 < μ t ∧ μ t < μ s`? --/ - -public section - -namespace MeasureTheory - -open Set Measure Filter TopologicalSpace - -variable {α : Type*} {m0 : MeasurableSpace α} {μ : Measure α} {s : Set α} - -/-- Measure `μ` *has no atoms* if the measure of each singleton is zero. - -NB: Wikipedia assumes that for any measurable set `s` with positive `μ`-measure, -there exists a measurable `t ⊆ s` such that `0 < μ t < μ s`. While this implies `μ {x} = 0`, -the converse is not true. -/ -class NoAtoms {m0 : MeasurableSpace α} (μ : Measure α) : Prop where - measure_singleton : ∀ x, μ {x} = 0 - -export MeasureTheory.NoAtoms (measure_singleton) - -attribute [simp] measure_singleton - -variable [NoAtoms μ] - -theorem _root_.Set.Subsingleton.measure_zero (hs : s.Subsingleton) (μ : Measure α) [NoAtoms μ] : - μ s = 0 := - hs.induction_on (p := fun s => μ s = 0) measure_empty measure_singleton - -theorem Measure.restrict_singleton' {a : α} : μ.restrict {a} = 0 := by - simp only [measure_singleton, Measure.restrict_eq_zero] - -instance Measure.restrict.instNoAtoms (s : Set α) : NoAtoms (μ.restrict s) := by - refine ⟨fun x => ?_⟩ - obtain ⟨t, hxt, ht1, ht2⟩ := exists_measurable_superset_of_null (measure_singleton x : μ {x} = 0) - apply measure_mono_null hxt - rw [Measure.restrict_apply ht1] - apply measure_mono_null inter_subset_left ht2 - -theorem _root_.Set.Countable.measure_zero (h : s.Countable) (μ : Measure α) [NoAtoms μ] : - μ s = 0 := by - rw [← biUnion_of_singleton s, measure_biUnion_null_iff h] - simp - -theorem _root_.Set.Countable.ae_notMem (h : s.Countable) (μ : Measure α) [NoAtoms μ] : - ∀ᵐ x ∂μ, x ∉ s := by - simpa only [ae_iff, Classical.not_not] using! h.measure_zero μ - -lemma Measure.ae_ne (μ : Measure α) [NoAtoms μ] (a : α) : ∀ᵐ x ∂μ, x ≠ a := - (countable_singleton a).ae_notMem μ - -lemma _root_.Set.Countable.measure_restrict_compl (h : s.Countable) (μ : Measure α) [NoAtoms μ] : - μ.restrict sᶜ = μ := - restrict_eq_self_of_ae_mem <| h.ae_notMem μ - -@[simp] -lemma restrict_compl_singleton (a : α) : μ.restrict ({a}ᶜ) = μ := - (countable_singleton _).measure_restrict_compl μ - -theorem _root_.Set.Finite.measure_zero (h : s.Finite) (μ : Measure α) [NoAtoms μ] : μ s = 0 := - h.countable.measure_zero μ - -theorem _root_.Finset.measure_zero (s : Finset α) (μ : Measure α) [NoAtoms μ] : μ s = 0 := - s.finite_toSet.measure_zero μ - -theorem insert_ae_eq_self (a : α) (s : Set α) : (insert a s : Set α) =ᵐ[μ] s := - union_ae_eq_right.2 <| measure_mono_null sdiff_subset (measure_singleton _) - -/- -If a set has positive measure under an atomless measure, then it has an accumulation point. +/-! # NoAtoms +This file is deprecated. Please use `Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass` +instead. -/ -theorem exists_accPt_of_noAtoms {X : Type*} [TopologicalSpace X] [MeasurableSpace X] - {μ : Measure X} [NoAtoms μ] {E : Set X} [SeparableSpace E] (hE : 0 < μ E) : - ∃ x, AccPt x (𝓟 E) := by - by_contra! h - haveI : DiscreteTopology E := discreteTopology_of_noAccPts fun x _ => h x - exact hE.ne' <| (Set.countable_coe_iff.mp <| separableSpace_iff_countable.mp ‹_›).measure_zero μ - -section - -variable [PartialOrder α] {a b : α} - -theorem Iio_ae_eq_Iic : Iio a =ᵐ[μ] Iic a := - Iio_ae_eq_Iic' (measure_singleton a) - -theorem Ioi_ae_eq_Ici : Ioi a =ᵐ[μ] Ici a := - Ioi_ae_eq_Ici' (measure_singleton a) - -theorem Ioo_ae_eq_Ioc : Ioo a b =ᵐ[μ] Ioc a b := - Ioo_ae_eq_Ioc' (measure_singleton b) - -theorem Ioc_ae_eq_Icc : Ioc a b =ᵐ[μ] Icc a b := - Ioc_ae_eq_Icc' (measure_singleton a) - -theorem Ioo_ae_eq_Ico : Ioo a b =ᵐ[μ] Ico a b := - Ioo_ae_eq_Ico' (measure_singleton a) - -theorem Ioo_ae_eq_Icc : Ioo a b =ᵐ[μ] Icc a b := - Ioo_ae_eq_Icc' (measure_singleton a) (measure_singleton b) - -theorem Ico_ae_eq_Icc : Ico a b =ᵐ[μ] Icc a b := - Ico_ae_eq_Icc' (measure_singleton b) - -theorem Ico_ae_eq_Ioc : Ico a b =ᵐ[μ] Ioc a b := - Ico_ae_eq_Ioc' (measure_singleton a) (measure_singleton b) - -theorem restrict_Iio_eq_restrict_Iic : μ.restrict (Iio a) = μ.restrict (Iic a) := - restrict_congr_set Iio_ae_eq_Iic - -theorem restrict_Ioi_eq_restrict_Ici : μ.restrict (Ioi a) = μ.restrict (Ici a) := - restrict_congr_set Ioi_ae_eq_Ici - -theorem restrict_Ioo_eq_restrict_Ioc : μ.restrict (Ioo a b) = μ.restrict (Ioc a b) := - restrict_congr_set Ioo_ae_eq_Ioc - -theorem restrict_Ioc_eq_restrict_Icc : μ.restrict (Ioc a b) = μ.restrict (Icc a b) := - restrict_congr_set Ioc_ae_eq_Icc - -theorem restrict_Ioo_eq_restrict_Ico : μ.restrict (Ioo a b) = μ.restrict (Ico a b) := - restrict_congr_set Ioo_ae_eq_Ico - -theorem restrict_Ioo_eq_restrict_Icc : μ.restrict (Ioo a b) = μ.restrict (Icc a b) := - restrict_congr_set Ioo_ae_eq_Icc - -theorem restrict_Ico_eq_restrict_Icc : μ.restrict (Ico a b) = μ.restrict (Icc a b) := - restrict_congr_set Ico_ae_eq_Icc - -theorem restrict_Ico_eq_restrict_Ioc : μ.restrict (Ico a b) = μ.restrict (Ioc a b) := - restrict_congr_set Ico_ae_eq_Ioc - -end - -open Interval - -open scoped Interval in -theorem uIoc_ae_eq_interval [LinearOrder α] {a b : α} : Ι a b =ᵐ[μ] [[a, b]] := - Ioc_ae_eq_Icc -end MeasureTheory +deprecated_module "use Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass instead" + (since := "2026-06-19") diff --git a/Mathlib/MeasureTheory/Measure/Typeclasses/NullSingletonClass.lean b/Mathlib/MeasureTheory/Measure/Typeclasses/NullSingletonClass.lean new file mode 100644 index 00000000000000..05a501fdba695a --- /dev/null +++ b/Mathlib/MeasureTheory/Measure/Typeclasses/NullSingletonClass.lean @@ -0,0 +1,163 @@ +/- +Copyright (c) 2017 Johannes Hölzl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Johannes Hölzl, Mario Carneiro +-/ +module + +public import Mathlib.MeasureTheory.Measure.Restrict +public import Mathlib.Topology.DiscreteSubset + +/-! +# Measures having value zero on singletons + +## TODO + +Add a `NoAtoms` class defined as +`∀ s, MeasurableSet s → 0 < μ s → ∃ t ⊆ s, MeasurableSet t ∧ 0 < μ t ∧ μ t < μ s`. +This implies `NullSingletonClass` but the converse is not true. +-/ + +public section + +namespace MeasureTheory + +open Set Measure Filter TopologicalSpace + +variable {α : Type*} {m0 : MeasurableSpace α} {μ : Measure α} {s : Set α} + +/-- Measure `μ` has value zero on singletons. -/ +class NullSingletonClass {m0 : MeasurableSpace α} (μ : Measure α) : Prop where + measure_singleton : ∀ x, μ {x} = 0 + +@[deprecated (since := "2026-06-09")] +alias NoAtoms := NullSingletonClass + +export MeasureTheory.NullSingletonClass (measure_singleton) + +attribute [simp] measure_singleton + +variable [NullSingletonClass μ] + +theorem _root_.Set.Subsingleton.measure_zero (hs : s.Subsingleton) (μ : Measure α) + [NullSingletonClass μ] : + μ s = 0 := + hs.induction_on (p := fun s => μ s = 0) measure_empty measure_singleton + +theorem Measure.restrict_singleton' {a : α} : μ.restrict {a} = 0 := by + simp only [measure_singleton, Measure.restrict_eq_zero] + +instance Measure.restrict.instNullSingletonClass (s : Set α) : + NullSingletonClass (μ.restrict s) := by + refine ⟨fun x => ?_⟩ + obtain ⟨t, hxt, ht1, ht2⟩ := exists_measurable_superset_of_null (measure_singleton x : μ {x} = 0) + apply measure_mono_null hxt + rw [Measure.restrict_apply ht1] + apply measure_mono_null inter_subset_left ht2 + +theorem _root_.Set.Countable.measure_zero (h : s.Countable) (μ : Measure α) [NullSingletonClass μ] : + μ s = 0 := by + rw [← biUnion_of_singleton s, measure_biUnion_null_iff h] + simp + +theorem _root_.Set.Countable.ae_notMem (h : s.Countable) (μ : Measure α) [NullSingletonClass μ] : + ∀ᵐ x ∂μ, x ∉ s := by + simpa only [ae_iff, Classical.not_not] using! h.measure_zero μ + +lemma Measure.ae_ne (μ : Measure α) [NullSingletonClass μ] (a : α) : ∀ᵐ x ∂μ, x ≠ a := + (countable_singleton a).ae_notMem μ + +lemma _root_.Set.Countable.measure_restrict_compl (h : s.Countable) (μ : Measure α) + [NullSingletonClass μ] : + μ.restrict sᶜ = μ := + restrict_eq_self_of_ae_mem <| h.ae_notMem μ + +@[simp] +lemma restrict_compl_singleton (a : α) : μ.restrict ({a}ᶜ) = μ := + (countable_singleton _).measure_restrict_compl μ + +theorem _root_.Set.Finite.measure_zero (h : s.Finite) (μ : Measure α) [NullSingletonClass μ] : + μ s = 0 := + h.countable.measure_zero μ + +theorem _root_.Finset.measure_zero (s : Finset α) (μ : Measure α) [NullSingletonClass μ] : + μ s = 0 := + s.finite_toSet.measure_zero μ + +theorem insert_ae_eq_self (a : α) (s : Set α) : (insert a s : Set α) =ᵐ[μ] s := + union_ae_eq_right.2 <| measure_mono_null sdiff_subset (measure_singleton _) + +/- +If a set has positive measure under an atomless measure, then it has an accumulation point. +-/ +theorem exists_accPt_of_nullSingletonClass {X : Type*} [TopologicalSpace X] [MeasurableSpace X] + {μ : Measure X} [NullSingletonClass μ] {E : Set X} [SeparableSpace E] (hE : 0 < μ E) : + ∃ x, AccPt x (𝓟 E) := by + by_contra! h + haveI : DiscreteTopology E := discreteTopology_of_noAccPts fun x _ => h x + exact hE.ne' <| (Set.countable_coe_iff.mp <| separableSpace_iff_countable.mp ‹_›).measure_zero μ + +@[deprecated (since := "2026-06-09")] +alias exists_accPt_of_noAtoms := exists_accPt_of_nullSingletonClass + +section + +variable [PartialOrder α] {a b : α} + +theorem Iio_ae_eq_Iic : Iio a =ᵐ[μ] Iic a := + Iio_ae_eq_Iic' (measure_singleton a) + +theorem Ioi_ae_eq_Ici : Ioi a =ᵐ[μ] Ici a := + Ioi_ae_eq_Ici' (measure_singleton a) + +theorem Ioo_ae_eq_Ioc : Ioo a b =ᵐ[μ] Ioc a b := + Ioo_ae_eq_Ioc' (measure_singleton b) + +theorem Ioc_ae_eq_Icc : Ioc a b =ᵐ[μ] Icc a b := + Ioc_ae_eq_Icc' (measure_singleton a) + +theorem Ioo_ae_eq_Ico : Ioo a b =ᵐ[μ] Ico a b := + Ioo_ae_eq_Ico' (measure_singleton a) + +theorem Ioo_ae_eq_Icc : Ioo a b =ᵐ[μ] Icc a b := + Ioo_ae_eq_Icc' (measure_singleton a) (measure_singleton b) + +theorem Ico_ae_eq_Icc : Ico a b =ᵐ[μ] Icc a b := + Ico_ae_eq_Icc' (measure_singleton b) + +theorem Ico_ae_eq_Ioc : Ico a b =ᵐ[μ] Ioc a b := + Ico_ae_eq_Ioc' (measure_singleton a) (measure_singleton b) + +theorem restrict_Iio_eq_restrict_Iic : μ.restrict (Iio a) = μ.restrict (Iic a) := + restrict_congr_set Iio_ae_eq_Iic + +theorem restrict_Ioi_eq_restrict_Ici : μ.restrict (Ioi a) = μ.restrict (Ici a) := + restrict_congr_set Ioi_ae_eq_Ici + +theorem restrict_Ioo_eq_restrict_Ioc : μ.restrict (Ioo a b) = μ.restrict (Ioc a b) := + restrict_congr_set Ioo_ae_eq_Ioc + +theorem restrict_Ioc_eq_restrict_Icc : μ.restrict (Ioc a b) = μ.restrict (Icc a b) := + restrict_congr_set Ioc_ae_eq_Icc + +theorem restrict_Ioo_eq_restrict_Ico : μ.restrict (Ioo a b) = μ.restrict (Ico a b) := + restrict_congr_set Ioo_ae_eq_Ico + +theorem restrict_Ioo_eq_restrict_Icc : μ.restrict (Ioo a b) = μ.restrict (Icc a b) := + restrict_congr_set Ioo_ae_eq_Icc + +theorem restrict_Ico_eq_restrict_Icc : μ.restrict (Ico a b) = μ.restrict (Icc a b) := + restrict_congr_set Ico_ae_eq_Icc + +theorem restrict_Ico_eq_restrict_Ioc : μ.restrict (Ico a b) = μ.restrict (Ioc a b) := + restrict_congr_set Ico_ae_eq_Ioc + +end + +open Interval + +open scoped Interval in +theorem uIoc_ae_eq_interval [LinearOrder α] {a b : α} : Ι a b =ᵐ[μ] [[a, b]] := + Ioc_ae_eq_Icc + +end MeasureTheory diff --git a/Mathlib/MeasureTheory/Measure/WithDensity.lean b/Mathlib/MeasureTheory/Measure/WithDensity.lean index 404bcf0aa490ea..a142193dbad040 100644 --- a/Mathlib/MeasureTheory/Measure/WithDensity.lean +++ b/Mathlib/MeasureTheory/Measure/WithDensity.lean @@ -5,7 +5,6 @@ Authors: Mario Carneiro, Johannes Hölzl -/ module -public import Mathlib.MeasureTheory.Integral.Lebesgue.Countable public import Mathlib.MeasureTheory.Measure.Decomposition.Exhaustion public import Mathlib.MeasureTheory.Group.Convolution public import Mathlib.Analysis.LConvolution @@ -160,9 +159,13 @@ theorem withDensity_apply₀ (f : α → ℝ≥0∞) {s : Set α} (hs : NullMeas rw [← A, ← B] exact withDensity_apply _ (measurableSet_toMeasurable μ s) -instance noAtoms_withDensity [NoAtoms μ] (f : α → ℝ≥0∞) : NoAtoms (μ.withDensity f) where +instance nullSingletonClass_withDensity [NullSingletonClass μ] (f : α → ℝ≥0∞) : + NullSingletonClass (μ.withDensity f) where measure_singleton _ := withDensity_absolutelyContinuous μ f (measure_singleton _) +@[deprecated (since := "2026-06-09")] +alias noAtoms_withDensity := nullSingletonClass_withDensity + @[simp] theorem withDensity_zero : μ.withDensity 0 = 0 := by ext1 s hs @@ -204,7 +207,7 @@ theorem withDensity_ofReal_mutuallySingular {f : α → ℝ} (hf : Measurable f) refine ⟨S, hS, ?_, ?_⟩ · rw [withDensity_apply _ hS, lintegral_eq_zero_iff hf.ennreal_ofReal, EventuallyEq] exact (ae_restrict_mem hS).mono fun x hx => ENNReal.ofReal_eq_zero.2 (le_of_lt hx) - · rw [withDensity_apply _ hS.compl, lintegral_eq_zero_iff hf.neg.ennreal_ofReal, EventuallyEq] + · rw [withDensity_apply _ hS.compl, lintegral_eq_zero_iff hf.fun_neg.ennreal_ofReal, EventuallyEq] exact (ae_restrict_mem hS.compl).mono fun x hx => ENNReal.ofReal_eq_zero.2 (not_lt.1 <| mt neg_pos.1 hx) @@ -399,10 +402,10 @@ theorem lintegral_withDensity_eq_lintegral_mul (μ : Measure α) {f : α → ℝ · intro c s h_ms simp [*, mul_comm _ c, ← indicator_mul_right] · intro g h _ h_mea_g _ h_ind_g h_ind_h - simp [mul_add, *, Measurable.mul] + simp [mul_add, *, Measurable.fun_mul] · intro g h_mea_g h_mono_g h_ind have : Monotone fun n a => f a * g n a := fun m n hmn x => by dsimp; grw [h_mono_g hmn x] - simp [lintegral_iSup, ENNReal.mul_iSup, h_mf.mul (h_mea_g _), *] + simp [lintegral_iSup, ENNReal.mul_iSup, h_mf.fun_mul (h_mea_g _), *] theorem setLIntegral_withDensity_eq_setLIntegral_mul (μ : Measure α) {f g : α → ℝ≥0∞} (hf : Measurable f) (hg : Measurable g) {s : Set α} (hs : MeasurableSet s) : @@ -482,9 +485,9 @@ theorem lintegral_withDensity_eq_lintegral_mul_non_measurable (μ : Measure α) dsimp rw [mul_comm, ← div_eq_mul_inv] exact div_le_of_le_mul' (hi x) - refine le_iSup_of_le (fun x => (f x)⁻¹ * i x) (le_iSup_of_le (f_meas.inv.mul i_meas) ?_) + refine le_iSup_of_le (fun x => (f x)⁻¹ * i x) (le_iSup_of_le (f_meas.fun_inv.mul i_meas) ?_) refine le_iSup_of_le A ?_ - rw [lintegral_withDensity_eq_lintegral_mul _ f_meas (f_meas.inv.mul i_meas)] + rw [lintegral_withDensity_eq_lintegral_mul _ f_meas (f_meas.fun_inv.fun_mul i_meas)] apply lintegral_mono_ae filter_upwards [hf] intro x h'x @@ -546,7 +549,7 @@ theorem withDensity_mul (μ : Measure α) {f g : α → ℝ≥0∞} (hf : Measur lemma withDensity_inv_same_le {μ : Measure α} {f : α → ℝ≥0∞} (hf : AEMeasurable f μ) : (μ.withDensity f).withDensity f⁻¹ ≤ μ := by change (μ.withDensity f).withDensity (fun x ↦ (f x)⁻¹) ≤ μ - rw [← withDensity_mul₀ hf hf.inv] + rw [← withDensity_mul₀ hf hf.fun_inv] suffices (f * fun x ↦ (f x)⁻¹) ≤ᵐ[μ] 1 by refine (withDensity_mono this).trans ?_ rw [withDensity_one] @@ -556,7 +559,7 @@ lemma withDensity_inv_same_le {μ : Measure α} {f : α → ℝ≥0∞} (hf : AE lemma withDensity_inv_same₀ {μ : Measure α} {f : α → ℝ≥0∞} (hf : AEMeasurable f μ) (hf_ne_zero : ∀ᵐ x ∂μ, f x ≠ 0) (hf_ne_top : ∀ᵐ x ∂μ, f x ≠ ∞) : (μ.withDensity f).withDensity (fun x ↦ (f x)⁻¹) = μ := by - rw [← withDensity_mul₀ hf hf.inv] + rw [← withDensity_mul₀ hf hf.fun_inv] suffices (f * fun x ↦ (f x)⁻¹) =ᵐ[μ] 1 by rw [withDensity_congr_ae this, withDensity_one] filter_upwards [hf_ne_zero, hf_ne_top] with x hf_ne_zero hf_ne_top @@ -660,8 +663,10 @@ instance Measure.withDensity.instSFinite [SFinite μ] {f : α → ℝ≥0∞} : rw [key] infer_instance -instance [SFinite μ] (c : ℝ≥0∞) : SFinite (c • μ) := by - rw [← withDensity_const] +instance [SFinite μ] {R : Type*} [SMul R ℝ≥0∞] [IsScalarTower R ℝ≥0∞ ℝ≥0∞] (c : R) : + SFinite (c • μ) := by + have : c • μ = c • ((1 : ℝ≥0∞) • μ) := by simp + rw [this, ← smul_assoc, ← withDensity_const] infer_instance /-- If `μ ≪ ν` and `ν` is s-finite, then `μ` is s-finite. -/ @@ -724,10 +729,12 @@ theorem prod_withDensity {f : α → ℝ≥0∞} {g : β → ℝ≥0∞} (hf : M -- `prod_smul_left` is in the `Prod` file. This lemma is here because this is the file in which -- we prove the instance that gives `SFinite (c • ν)`. -lemma Measure.prod_smul_right (c : ℝ≥0∞) : μ.prod (c • ν) = c • (μ.prod ν) := by +lemma Measure.prod_smul_right {R : Type*} [SMul R ℝ≥0∞] [IsScalarTower R ℝ≥0∞ ℝ≥0∞] (c : R) : + μ.prod (c • ν) = c • (μ.prod ν) := by ext s hs - simp_rw [Measure.prod_apply hs, Measure.smul_apply, Measure.prod_apply hs, smul_eq_mul] - rw [lintegral_const_mul] + have A (s : Set β) : c • ν s = (c • 1) * ν s := by simp + simp_rw [Measure.prod_apply hs, Measure.smul_apply, Measure.prod_apply hs, A] + rw [lintegral_const_mul, smul_one_mul] exact measurable_measure_prodMk_left hs end Prod diff --git a/Mathlib/MeasureTheory/Order/Group/Lattice.lean b/Mathlib/MeasureTheory/Order/Group/Lattice.lean index 91276c853ca089..0f9321d2d36a79 100644 --- a/Mathlib/MeasureTheory/Order/Group/Lattice.lean +++ b/Mathlib/MeasureTheory/Order/Group/Lattice.lean @@ -5,7 +5,6 @@ Authors: Xavier Roblot -/ module -public import Mathlib.Algebra.Order.Group.PosPart public import Mathlib.MeasureTheory.Group.Arithmetic public import Mathlib.MeasureTheory.Order.Lattice diff --git a/Mathlib/MeasureTheory/Order/Lattice.lean b/Mathlib/MeasureTheory/Order/Lattice.lean index a699073554a52c..ba451d7bb82e49 100644 --- a/Mathlib/MeasureTheory/Order/Lattice.lean +++ b/Mathlib/MeasureTheory/Order/Lattice.lean @@ -120,23 +120,18 @@ section MeasurableSup₂ variable [MeasurableSup₂ M] -@[fun_prop] -theorem Measurable.sup' (hf : Measurable f) (hg : Measurable g) : Measurable (f ⊔ g) := +@[to_fun (attr := fun_prop)] +theorem Measurable.sup (hf : Measurable f) (hg : Measurable g) : Measurable (f ⊔ g) := measurable_sup.comp (hf.prodMk hg) -@[fun_prop] -theorem Measurable.sup (hf : Measurable f) (hg : Measurable g) : Measurable fun a => f a ⊔ g a := - measurable_sup.comp (hf.prodMk hg) +@[deprecated (since := "2026-06-26")] alias Measurable.sup' := Measurable.sup -@[fun_prop] -theorem AEMeasurable.sup' (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : +@[to_fun (attr := fun_prop)] +theorem AEMeasurable.sup (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : AEMeasurable (f ⊔ g) μ := measurable_sup.comp_aemeasurable (hf.prodMk hg) -@[fun_prop] -theorem AEMeasurable.sup (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : - AEMeasurable (fun a => f a ⊔ g a) μ := - measurable_sup.comp_aemeasurable (hf.prodMk hg) +@[deprecated (since := "2026-06-26")] alias AEMeasurable.sup' := AEMeasurable.sup instance (priority := 100) MeasurableSup₂.toMeasurableSup : MeasurableSup M where @@ -176,23 +171,18 @@ section MeasurableInf₂ variable [MeasurableInf₂ M] -@[fun_prop] -theorem Measurable.inf' (hf : Measurable f) (hg : Measurable g) : Measurable (f ⊓ g) := +@[to_fun (attr := fun_prop)] +theorem Measurable.inf (hf : Measurable f) (hg : Measurable g) : Measurable (f ⊓ g) := measurable_inf.comp (hf.prodMk hg) -@[fun_prop] -theorem Measurable.inf (hf : Measurable f) (hg : Measurable g) : Measurable fun a => f a ⊓ g a := - measurable_inf.comp (hf.prodMk hg) +@[deprecated (since := "2026-06-26")] alias Measurable.inf' := Measurable.inf -@[fun_prop] -theorem AEMeasurable.inf' (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : +@[to_fun (attr := fun_prop)] +theorem AEMeasurable.inf (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : AEMeasurable (f ⊓ g) μ := measurable_inf.comp_aemeasurable (hf.prodMk hg) -@[fun_prop] -theorem AEMeasurable.inf (hf : AEMeasurable f μ) (hg : AEMeasurable g μ) : - AEMeasurable (fun a => f a ⊓ g a) μ := - measurable_inf.comp_aemeasurable (hf.prodMk hg) +@[deprecated (since := "2026-06-26")] alias AEMeasurable.inf' := AEMeasurable.inf instance (priority := 100) MeasurableInf₂.to_hasMeasurableInf : MeasurableInf M where diff --git a/Mathlib/MeasureTheory/Order/UpperLower.lean b/Mathlib/MeasureTheory/Order/UpperLower.lean index a53a76ce446326..a0fb96c8e8f241 100644 --- a/Mathlib/MeasureTheory/Order/UpperLower.lean +++ b/Mathlib/MeasureTheory/Order/UpperLower.lean @@ -7,7 +7,6 @@ module public import Mathlib.Analysis.Normed.Order.UpperLower public import Mathlib.MeasureTheory.Covering.BesicovitchVectorSpace -public import Mathlib.Topology.Order.DenselyOrdered /-! # Order-connected sets are null-measurable diff --git a/Mathlib/MeasureTheory/OuterMeasure/Basic.lean b/Mathlib/MeasureTheory/OuterMeasure/Basic.lean index bbc0b368cd857e..b9bf43ad126a20 100644 --- a/Mathlib/MeasureTheory/OuterMeasure/Basic.lean +++ b/Mathlib/MeasureTheory/OuterMeasure/Basic.lean @@ -5,9 +5,6 @@ Authors: Johannes Hölzl, Mario Carneiro -/ module -public import Mathlib.Data.Countable.Basic -public import Mathlib.Data.Fin.VecNotation -public import Mathlib.Order.Disjointed public import Mathlib.MeasureTheory.OuterMeasure.Defs public import Mathlib.Topology.Algebra.InfiniteSum.ENNReal diff --git a/Mathlib/MeasureTheory/OuterMeasure/Induced.lean b/Mathlib/MeasureTheory/OuterMeasure/Induced.lean index 7754fe892632d7..fdf6b35f557d89 100644 --- a/Mathlib/MeasureTheory/OuterMeasure/Induced.lean +++ b/Mathlib/MeasureTheory/OuterMeasure/Induced.lean @@ -437,7 +437,7 @@ theorem trim_add (m₁ m₂ : OuterMeasure α) : (m₁ + m₂).trim = m₁.trim /-- `trim` respects scalar multiplication. -/ theorem trim_smul {R : Type*} [SMul R ℝ≥0∞] [IsScalarTower R ℝ≥0∞ ℝ≥0∞] (c : R) (m : OuterMeasure α) : (c • m).trim = c • m.trim := - ext <| trim_op (smul_apply c m) + ext <| trim_op (smul_apply m c) /-- `trim` sends the supremum of two outer measures to the supremum of the trimmed measures. -/ theorem trim_sup (m₁ m₂ : OuterMeasure α) : (m₁ ⊔ m₂).trim = m₁.trim ⊔ m₂.trim := diff --git a/Mathlib/MeasureTheory/OuterMeasure/OfAddContent.lean b/Mathlib/MeasureTheory/OuterMeasure/OfAddContent.lean index c93d32bd045461..239c7d93657e88 100644 --- a/Mathlib/MeasureTheory/OuterMeasure/OfAddContent.lean +++ b/Mathlib/MeasureTheory/OuterMeasure/OfAddContent.lean @@ -5,7 +5,6 @@ Authors: Rémy Degenne, Peter Pfaffelhuber -/ module -public import Mathlib.MeasureTheory.SetSemiring public import Mathlib.MeasureTheory.Measure.AddContent public import Mathlib.MeasureTheory.Measure.Trim diff --git a/Mathlib/MeasureTheory/OuterMeasure/Operations.lean b/Mathlib/MeasureTheory/OuterMeasure/Operations.lean index 895650c2ddcde1..e928f1711c0649 100644 --- a/Mathlib/MeasureTheory/OuterMeasure/Operations.lean +++ b/Mathlib/MeasureTheory/OuterMeasure/Operations.lean @@ -7,6 +7,7 @@ module public import Mathlib.Algebra.Order.Group.Indicator public import Mathlib.MeasureTheory.OuterMeasure.Basic +public import Mathlib.Data.FunLike.Module /-! # Operations on outer measures @@ -45,9 +46,10 @@ instance instZero : Zero (OuterMeasure α) := mono _ := le_rfl iUnion_nat _ _ := zero_le }⟩ -@[simp] -theorem coe_zero : ⇑(0 : OuterMeasure α) = 0 := - rfl +instance : IsZeroApply (OuterMeasure α) (Set α) ℝ≥0∞ where + zero_apply _ := rfl + +@[deprecated (since := "2026-06-23")] alias coe_zero := FunLike.coe_zero instance instInhabited : Inhabited (OuterMeasure α) := ⟨0⟩ @@ -63,12 +65,12 @@ instance instAdd : Add (OuterMeasure α) := add_le_add (measure_iUnion_le s) (measure_iUnion_le s) _ = _ := ENNReal.tsum_add.symm }⟩ -@[simp] -theorem coe_add (m₁ m₂ : OuterMeasure α) : ⇑(m₁ + m₂) = m₁ + m₂ := - rfl +instance : IsAddApply (OuterMeasure α) (Set α) ℝ≥0∞ where + add_apply _ _ _ := rfl -theorem add_apply (m₁ m₂ : OuterMeasure α) (s : Set α) : (m₁ + m₂) s = m₁ s + m₂ s := - rfl +@[deprecated (since := "2026-06-23")] alias coe_add := FunLike.coe_add + +@[deprecated (since := "2026-06-23")] protected alias add_apply := add_apply section SMul @@ -86,49 +88,39 @@ instance instSMul : SMul R (OuterMeasure α) := simp_rw [← smul_one_mul c (m _), ENNReal.tsum_mul_left] exact mul_right_mono (measure_iUnion_le _) }⟩ -@[simp] -theorem coe_smul (c : R) (m : OuterMeasure α) : ⇑(c • m) = c • ⇑m := - rfl +instance : IsSMulApply R (OuterMeasure α) (Set α) ℝ≥0∞ where + smul_apply _ _ _ := rfl -theorem smul_apply (c : R) (m : OuterMeasure α) (s : Set α) : (c • m) s = c • m s := - rfl +@[deprecated (since := "2026-06-23")] alias coe_smul := FunLike.coe_smul + +@[deprecated (since := "2026-06-23")] protected alias smul_apply := smul_apply instance instSMulCommClass [SMulCommClass R R' ℝ≥0∞] : SMulCommClass R R' (OuterMeasure α) := - ⟨fun _ _ _ => ext fun _ => smul_comm _ _ _⟩ + FunLike.smulCommClass instance instIsScalarTower [SMul R R'] [IsScalarTower R R' ℝ≥0∞] : - IsScalarTower R R' (OuterMeasure α) := - ⟨fun _ _ _ => ext fun _ => smul_assoc _ _ _⟩ + IsScalarTower R R' (OuterMeasure α) := FunLike.isScalarTower instance instIsCentralScalar [SMul Rᵐᵒᵖ ℝ≥0∞] [IsCentralScalar R ℝ≥0∞] : - IsCentralScalar R (OuterMeasure α) := - ⟨fun _ _ => ext fun _ => op_smul_eq_smul _ _⟩ + IsCentralScalar R (OuterMeasure α) := FunLike.isCentralScalar end SMul instance instMulAction {R : Type*} [Monoid R] [MulAction R ℝ≥0∞] [IsScalarTower R ℝ≥0∞ ℝ≥0∞] : - MulAction R (OuterMeasure α) := - Injective.mulAction _ coe_fn_injective coe_smul + MulAction R (OuterMeasure α) := fast_instance% FunLike.mulAction + +instance addCommMonoid : AddCommMonoid (OuterMeasure α) := fast_instance% FunLike.addCommMonoid -instance addCommMonoid : AddCommMonoid (OuterMeasure α) := - Injective.addCommMonoid (show OuterMeasure α → Set α → ℝ≥0∞ from _) coe_fn_injective rfl - (fun _ _ => rfl) fun _ _ => rfl +@[deprecated (since := "2026-06-23")] alias coeFnAddMonoidHom := FunLike.coeAddMonoidHom -/-- `(⇑)` as an `AddMonoidHom`. -/ -@[simps] -def coeFnAddMonoidHom : OuterMeasure α →+ Set α → ℝ≥0∞ where - toFun := (⇑) - map_zero' := coe_zero - map_add' := coe_add +@[deprecated (since := "2026-06-23")] alias coeFnAddMonoidHom_apply := FunLike.coeAddMonoidHom_apply instance instDistribMulAction {R : Type*} [Monoid R] [DistribMulAction R ℝ≥0∞] [IsScalarTower R ℝ≥0∞ ℝ≥0∞] : - DistribMulAction R (OuterMeasure α) := - Injective.distribMulAction coeFnAddMonoidHom coe_fn_injective coe_smul + DistribMulAction R (OuterMeasure α) := fast_instance% FunLike.distribMulAction instance instModule {R : Type*} [Semiring R] [Module R ℝ≥0∞] [IsScalarTower R ℝ≥0∞ ℝ≥0∞] : - Module R (OuterMeasure α) := - Injective.module R coeFnAddMonoidHom coe_fn_injective coe_smul + Module R (OuterMeasure α) := fast_instance% FunLike.module instance instBot : Bot (OuterMeasure α) := ⟨0⟩ @@ -148,7 +140,7 @@ instance instIsOrderedAddMonoid {α : Type*} : IsOrderedAddMonoid (OuterMeasure instance orderBot : OrderBot (OuterMeasure α) := { bot := 0, - bot_le := fun a s => by simp only [coe_zero, Pi.zero_apply, zero_le] } + bot_le := fun a s => by simp only [zero_apply, zero_le] } theorem univ_eq_zero_iff (m : OuterMeasure α) : m univ = 0 ↔ m = 0 := ⟨fun h => bot_unique fun s => (measure_mono <| subset_univ s).trans_eq h, fun h => h.symm ▸ rfl⟩ @@ -346,7 +338,7 @@ theorem comap_map {β} {f : α → β} (hf : Injective f) (m : OuterMeasure α) @[simp] theorem top_apply {s : Set α} (h : s.Nonempty) : (⊤ : OuterMeasure α) s = ∞ := let ⟨a, as⟩ := h - top_unique <| le_trans (by simp [smul_dirac_apply, as]) (le_iSup₂ (∞ • dirac a) trivial) + top_unique <| le_trans (by simp [as]) (le_iSup₂ (∞ • dirac a) trivial) theorem top_apply' (s : Set α) : (⊤ : OuterMeasure α) s = ⨅ _ : s = ∅, 0 := s.eq_empty_or_nonempty.elim (fun h => by simp [h]) fun h => by simp [h, h.ne_empty] diff --git a/Mathlib/MeasureTheory/SetSemiring.lean b/Mathlib/MeasureTheory/SetSemiring.lean index 55d406b20306b8..6c8c36d2502f7f 100644 --- a/Mathlib/MeasureTheory/SetSemiring.lean +++ b/Mathlib/MeasureTheory/SetSemiring.lean @@ -5,11 +5,8 @@ Authors: Rémy Degenne, Peter Pfaffelhuber -/ module -public import Mathlib.Data.Set.Pairwise.Lattice public import Mathlib.MeasureTheory.PiSystem -public import Mathlib.Order.Lattice.Nat public import Mathlib.Order.Partition.Finpartition -public import Mathlib.Order.SetAccumulate public import Mathlib.Order.SupClosed /-! # Semirings and rings of sets diff --git a/Mathlib/MeasureTheory/SpecificCodomains/ContinuousMap.lean b/Mathlib/MeasureTheory/SpecificCodomains/ContinuousMap.lean index 7b5d033e1a0802..3f7e457ac04f41 100644 --- a/Mathlib/MeasureTheory/SpecificCodomains/ContinuousMap.lean +++ b/Mathlib/MeasureTheory/SpecificCodomains/ContinuousMap.lean @@ -6,7 +6,6 @@ Authors: Anatole Dedecker module public import Mathlib.Topology.ContinuousMap.Compact -public import Mathlib.Topology.ContinuousMap.Algebra public import Mathlib.MeasureTheory.Integral.IntegrableOn /-! diff --git a/Mathlib/MeasureTheory/Topology.lean b/Mathlib/MeasureTheory/Topology.lean index f411e33eff8304..0775598d56be28 100644 --- a/Mathlib/MeasureTheory/Topology.lean +++ b/Mathlib/MeasureTheory/Topology.lean @@ -5,7 +5,7 @@ Authors: Stefan Kebekus -/ module -public import Mathlib.MeasureTheory.Measure.Typeclasses.NoAtoms +public import Mathlib.MeasureTheory.Measure.Typeclasses.NullSingletonClass public import Mathlib.Topology.DiscreteSubset /-! @@ -22,7 +22,7 @@ open Filter MeasureTheory everywhere" filter of co-null sets. -/ theorem ae_restrict_le_codiscreteWithin {α : Type*} [MeasurableSpace α] [TopologicalSpace α] [SecondCountableTopology α] - {μ : Measure α} [NoAtoms μ] {U : Set α} (hU : MeasurableSet U) : + {μ : Measure α} [NullSingletonClass μ] {U : Set α} (hU : MeasurableSet U) : ae (μ.restrict U) ≤ codiscreteWithin U := by intro s hs have : DiscreteTopology ↑(sᶜ ∩ U) := isDiscrete_iff_discreteTopology.mp diff --git a/Mathlib/MeasureTheory/VectorMeasure/Basic.lean b/Mathlib/MeasureTheory/VectorMeasure/Basic.lean index 3f8ee9b91f4a05..ace2e7cfc59cc5 100644 --- a/Mathlib/MeasureTheory/VectorMeasure/Basic.lean +++ b/Mathlib/MeasureTheory/VectorMeasure/Basic.lean @@ -6,7 +6,6 @@ Authors: Kexing Ying module public import Mathlib.MeasureTheory.Measure.Real -public import Mathlib.MeasureTheory.Measure.Typeclasses.Finite public import Mathlib.Topology.Algebra.InfiniteSum.Module /-! diff --git a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean index 79ff56ab497813..c654aaef8b65ad 100644 --- a/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean +++ b/Mathlib/MeasureTheory/VectorMeasure/Decomposition/Hahn.lean @@ -6,7 +6,6 @@ Authors: Kexing Ying module public import Mathlib.MeasureTheory.VectorMeasure.Basic -public import Mathlib.Order.SymmDiff /-! # Hahn decomposition diff --git a/Mathlib/MeasureTheory/VectorMeasure/Integral.lean b/Mathlib/MeasureTheory/VectorMeasure/Integral.lean index bc0a720f0b4a69..d82e901ee3ed54 100644 --- a/Mathlib/MeasureTheory/VectorMeasure/Integral.lean +++ b/Mathlib/MeasureTheory/VectorMeasure/Integral.lean @@ -174,7 +174,7 @@ lemma variation_transpose_le : apply opENorm_le_bound _ (fun x ↦ ?_) simp only [transpose, mapRange_apply, LinearMap.toAddMonoidHom_coe, coe_coe, flip_apply, Measure.smul_apply, Measure.nnreal_smul_coe_apply] - grw [le_opNorm_enorm, le_opNorm_enorm, enorm_measure_le_variation, ← enorm_eq_nnnorm] + grw [le_opENorm, le_opENorm, enorm_measure_le_variation, ← enorm_eq_nnnorm] exact le_of_eq (by ring) lemma absolutelyContinuous_variation_transpose (μ : VectorMeasure X F) (B : E →L[ℝ] F →L[ℝ] G) : diff --git a/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean b/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean new file mode 100644 index 00000000000000..47c85aa59371b4 --- /dev/null +++ b/Mathlib/MeasureTheory/VectorMeasure/SetIntegral.lean @@ -0,0 +1,592 @@ +/- +Copyright (c) 2026 Sébastien Gouëzel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sébastien Gouëzel +-/ +module + +public import Mathlib.MeasureTheory.Integral.IntegrableOn +public import Mathlib.MeasureTheory.VectorMeasure.Integral + +/-! +# Set integral + +In this file we prove properties of `∫ᵛ x in s, f x ∂[B; μ]`. Recall that this notation +is defined as `∫ᵛ x, f x ∂[B; μ.restrict s]`. + +The API in this file is modelled on the API for the Bochner integral. +-/ + +@[expose] public section + +assert_not_exists InnerProductSpace + +open Filter Function MeasureTheory RCLike Set TopologicalSpace Topology ContinuousLinearMap +open scoped ENNReal NNReal Finset + +variable {ι X E F G H : Type*} {mX : MeasurableSpace X} + [NormedAddCommGroup E] [NormedAddCommGroup F] [NormedAddCommGroup G] [NormedAddCommGroup H] + {μ ν : VectorMeasure X F} {f g : X → E} {s t : Set X} + +namespace MeasureTheory.VectorMeasure + +theorem IntegrableOn.mono (hs : MeasurableSet s) (hts : t ⊆ s) (h : μ.IntegrableOn f s) : + μ.IntegrableOn f t := by + by_cases ht : MeasurableSet t; swap + · simp [VectorMeasure.IntegrableOn, restrict_not_measurable _ ht] + apply Integrable.mono_measure h + simp [variation_restrict, hs, ht, Measure.restrict_mono hts le_rfl] + +theorem IntegrableOn.union (hs : MeasurableSet s) (ht : MeasurableSet t) + (hf : μ.IntegrableOn f s) (h'f : μ.IntegrableOn f t) : + μ.IntegrableOn f (s ∪ t) := by + apply Integrable.mono_measure (hf.add_measure h'f) + grw [variation_restrict_le, Measure.restrict_union_le] + simp [variation_restrict, hs, ht] + +/- `simpNF` complains that this lemma can be proved by `simp`, because the `simp`-generated lemma +unfolds the abbrev `VectorMeasure.Integrable`. TODO: fix `simp`. See lean4#13958. -/ +@[simp, nolint simpNF] theorem IntegrableOn.empty : μ.IntegrableOn f ∅ := by + simp [VectorMeasure.IntegrableOn] + +theorem IntegrableOn.biUnion_finite + {s : Set ι} (hs : s.Finite) {t : ι → Set X} (ht : ∀ i ∈ s, MeasurableSet (t i)) + (h't : ∀ i ∈ s, μ.IntegrableOn f (t i)) : + μ.IntegrableOn f (⋃ i ∈ s, t i) := by + induction s, hs using Set.Finite.induction_on with + | empty => simp + | insert _ h's hf => + simp only [mem_insert_iff, forall_eq_or_imp, iUnion_iUnion_eq_or_left] at ht h't ⊢ + exact IntegrableOn.union ht.1 (h's.measurableSet_biUnion ht.2) h't.1 (hf ht.2 h't.2) + +theorem IntegrableOn.biUnion_finset {s : Finset ι} {t : ι → Set X} + (ht : ∀ i ∈ s, MeasurableSet (t i)) (h't : ∀ i ∈ s, μ.IntegrableOn f (t i)) : + μ.IntegrableOn f (⋃ i ∈ s, t i) := + IntegrableOn.biUnion_finite s.finite_toSet ht h't + +theorem IntegrableOn.iUnion_finite [Finite ι] {t : ι → Set X} + (ht : ∀ i, MeasurableSet (t i)) (h't : ∀ i, μ.IntegrableOn f (t i)) : + μ.IntegrableOn f (⋃ i, t i) := by + cases nonempty_fintype ι + simpa using IntegrableOn.biUnion_finset (f := f) (μ := μ) (s := Finset.univ) (t := t) + (fun i hi ↦ ht i) (fun i hi ↦ h't i) + +@[simp] theorem integrableOn_univ : μ.IntegrableOn f univ ↔ μ.Integrable f := by + simp [VectorMeasure.IntegrableOn] + +theorem Integrable.integrableOn (h : μ.Integrable f) : μ.IntegrableOn f s := by + rw [← integrableOn_univ] at h + exact h.mono MeasurableSet.univ (subset_univ _) + +theorem integrable_indicator_iff (hs : MeasurableSet s) : + μ.Integrable (indicator s f) ↔ μ.IntegrableOn f s := by + simp [VectorMeasure.Integrable, VectorMeasure.IntegrableOn, MeasureTheory.IntegrableOn, + MeasureTheory.integrable_indicator_iff hs, variation_restrict hs] + +theorem IntegrableOn.integrable_indicator (h : μ.IntegrableOn f s) (hs : MeasurableSet s) : + μ.Integrable (indicator s f) := + (integrable_indicator_iff hs).2 h + +variable [NormedSpace ℝ E] [NormedSpace ℝ F] [NormedSpace ℝ G] [NormedSpace ℝ H] + {B : E →L[ℝ] F →L[ℝ] G} + +theorem setIntegral_eq_zero_of_not_measurableSet (hs : ¬MeasurableSet s) : + ∫ᵛ x in s, f x ∂[B; μ] = 0 := by + simp [restrict_not_measurable _ hs] + +theorem setIntegral_congr_ae (h : ∀ᵐ x ∂μ.variation, x ∈ s → f x = g x) : + ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x in s, g x ∂[B; μ] := by + by_cases hs : MeasurableSet s; swap + · simp [setIntegral_eq_zero_of_not_measurableSet hs] + apply integral_congr_ae + rw [variation_restrict hs] + exact (ae_restrict_iff' hs).2 h + +theorem setIntegral_congr_fun (h : EqOn f g s) : + ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x in s, g x ∂[B; μ] := + setIntegral_congr_ae <| Eventually.of_forall h + +theorem setIntegral_union (hst : Disjoint s t) (hs : MeasurableSet s) (ht : MeasurableSet t) + (hfs : μ.IntegrableOn f s) (hft : μ.IntegrableOn f t) : + ∫ᵛ x in s ∪ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in t, f x ∂[B; μ] := by + rw [← integral_add_vectorMeasure hfs hft, μ.restrict_union hst hs ht] + +theorem setIntegral_sdiff (hs : MeasurableSet s) (ht : MeasurableSet t) + (hfs : μ.IntegrableOn f s) (hts : t ⊆ s) : + ∫ᵛ x in s \ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] - ∫ᵛ x in t, f x ∂[B; μ] := by + rw [eq_sub_iff_add_eq, ← setIntegral_union (by grind) (hs.diff ht) ht (hfs.mono hs sdiff_subset) + (hfs.mono hs hts), sdiff_union_of_subset hts] + +theorem setIntegral_inter_add_sdiff (hs : MeasurableSet s) (ht : MeasurableSet t) + (hfs : μ.IntegrableOn f s) : + ∫ᵛ x in s ∩ t, f x ∂[B; μ] + ∫ᵛ x in s \ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by + rw [← μ.restrict_inter_add_sdiff hs ht, + integral_add_vectorMeasure (hfs.mono hs inter_subset_left) (hfs.mono hs sdiff_subset)] + +theorem setIntegral_biUnion_finset {ι : Type*} (t : Finset ι) {s : ι → Set X} + (hs : ∀ i ∈ t, MeasurableSet (s i)) (h's : Set.Pairwise (↑t) (Disjoint on s)) + (hf : ∀ i ∈ t, μ.IntegrableOn f (s i)) : + ∫ᵛ x in ⋃ i ∈ t, s i, f x ∂[B; μ] = ∑ i ∈ t, ∫ᵛ x in s i, f x ∂[B; μ] := by + classical + induction t using Finset.induction_on with + | empty => simp + | insert _ _ hat IH => + simp only [Finset.coe_insert, Finset.forall_mem_insert, Set.pairwise_insert, + Finset.set_biUnion_insert] at hs hf h's ⊢ + rw [setIntegral_union] + · rw [Finset.sum_insert hat, IH hs.2 h's.1 hf.2] + · simp only [disjoint_iUnion_right] + exact fun i hi => (h's.2 i hi (ne_of_mem_of_not_mem hi hat).symm).1 + · exact hs.1 + · exact Finset.measurableSet_biUnion _ hs.2 + · exact hf.1 + · apply IntegrableOn.biUnion_finset hs.2 hf.2 + +theorem setIntegral_iUnion_fintype {ι : Type*} [Fintype ι] {s : ι → Set X} + (hs : ∀ i, MeasurableSet (s i)) (h's : Pairwise (Disjoint on s)) + (hf : ∀ i, μ.IntegrableOn f (s i)) : + ∫ᵛ x in ⋃ i, s i, f x ∂[B; μ] = ∑ i, ∫ᵛ x in s i, f x ∂[B; μ] := by + convert setIntegral_biUnion_finset Finset.univ (fun i _ => hs i) _ fun i _ => hf i + · simp + · simp [pairwise_univ, h's] + +theorem setIntegral_empty : ∫ᵛ x in ∅, f x ∂[B; μ] = 0 := by simp + +theorem setIntegral_univ : ∫ᵛ x in univ, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] := by simp + +theorem setIntegral_add_compl (hs : MeasurableSet s) (hfi : μ.Integrable f) : + ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in sᶜ, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] := by + rw [← setIntegral_union disjoint_compl_right + hs hs.compl hfi.integrableOn hfi.integrableOn, union_compl_self, setIntegral_univ] + +theorem setIntegral_compl (hs : MeasurableSet s) (hfi : μ.Integrable f) : + ∫ᵛ x in sᶜ, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] - ∫ᵛ x in s, f x ∂[B; μ] := by + rw [← setIntegral_add_compl (μ := μ) hs hfi, add_sub_cancel_left] + +/-- For a function `f` and a measurable set `s`, the integral of `indicator s f` +over the whole space is equal to `∫ᵛ x in s, f x ∂[B; μ]` +defined as `∫ᵛ x, f x ∂[B; μ.restrict s]`. -/ +theorem integral_indicator (hs : MeasurableSet s) : + ∫ᵛ x, indicator s f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by + by_cases hfi : μ.IntegrableOn f s; swap + · rw [integral_undef hfi, integral_undef] + rw [integrable_indicator_iff hs] + simpa [transpose_restrict, variation_restrict hs] using hfi + calc + ∫ᵛ x, indicator s f x ∂[B; μ] + _ = ∫ᵛ x in s, indicator s f x ∂[B; μ] + ∫ᵛ x in sᶜ, indicator s f x ∂[B; μ] := + (setIntegral_add_compl hs (hfi.integrable_indicator hs)).symm + _ = ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in sᶜ, 0 ∂[B; μ] := by + apply congr_arg₂ (· + ·) (integral_congr_ae ?_) (integral_congr_ae ?_) + · rw [variation_restrict hs] + exact indicator_ae_eq_restrict hs + · rw [variation_restrict hs.compl] + exact indicator_ae_eq_restrict_compl hs + _ = ∫ᵛ x in s, f x ∂[B; μ] := by simp + +theorem setIntegral_indicator (hs : MeasurableSet s) (ht : MeasurableSet t) : + ∫ᵛ x in s, t.indicator f x ∂[B; μ] = ∫ᵛ x in s ∩ t, f x ∂[B; μ] := by + rw [integral_indicator ht, μ.restrict_restrict ht hs, Set.inter_comm] + +theorem setIntegral_congr_set + (hs : MeasurableSet s) (ht : MeasurableSet t) (hst : s =ᵐ[μ.variation] t) : + ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x in t, f x ∂[B; μ] := by + rw [← integral_indicator hs, ← integral_indicator ht] + apply integral_congr_ae + filter_upwards [hst] with x hx + replace hx : x ∈ s ↔ x ∈ t := by simpa using! hx + simp [indicator] + grind + +theorem integral_piecewise [DecidablePred (· ∈ s)] + (hs : MeasurableSet s) (hf : μ.IntegrableOn f s) (hg : μ.IntegrableOn g sᶜ) : + ∫ᵛ x, s.piecewise f g x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] + ∫ᵛ x in sᶜ, g x ∂[B; μ] := by + rw [← Set.indicator_add_compl_eq_piecewise, + integral_add (hf.integrable_indicator hs) (hg.integrable_indicator hs.compl), + integral_indicator hs, integral_indicator hs.compl] + +theorem setIntegral_eq_zero_of_ae_eq_zero + (ht_eq : ∀ᵐ x ∂μ.variation, x ∈ t → f x = 0) : + ∫ᵛ x in t, f x ∂[B; μ] = 0 := by + by_cases ht : MeasurableSet t; swap + · simp [setIntegral_eq_zero_of_not_measurableSet ht] + by_cases hf : AEStronglyMeasurable f (μ.restrict t).variation; swap + · rw [integral_undef] + contrapose hf + exact hf.1 + simp only [variation_restrict ht] at hf + have : ∫ᵛ x in t, hf.mk f x ∂[B; μ] = 0 := by + refine integral_eq_zero_of_ae ?_ + simp only [variation_restrict ht] + apply (ae_restrict_iff' ht).2 + filter_upwards [ae_imp_of_ae_restrict hf.ae_eq_mk, ht_eq] with x hx h'x h''x + rw [← hx h''x] + exact h'x h''x + rw [← this] + apply integral_congr_ae + simp only [variation_restrict ht] + exact hf.ae_eq_mk + +theorem setIntegral_eq_zero_of_forall_eq_zero (ht_eq : ∀ x ∈ t, f x = 0) : + ∫ᵛ x in t, f x ∂[B; μ] = 0 := + setIntegral_eq_zero_of_ae_eq_zero (Eventually.of_forall ht_eq) + +theorem frequently_ae_ne_zero_of_setIntegral_ne_zero (hU : ∫ᵛ x in t, f x ∂[B; μ] ≠ 0) : + ∃ᶠ x in ae (μ.variation.restrict t), f x ≠ 0 := by + have ht : MeasurableSet t := by + contrapose! hU + simp [setIntegral_eq_zero_of_not_measurableSet hU] + rw [← variation_restrict ht] + exact frequently_ae_ne_zero_of_integral_ne_zero hU + +theorem exists_ne_zero_of_setIntegral_ne_zero (hU : ∫ᵛ x in t, f x ∂[B; μ] ≠ 0) : + ∃ x, x ∈ t ∧ f x ≠ 0 := by + contrapose! hU; exact setIntegral_eq_zero_of_forall_eq_zero hU + +theorem setIntegral_of_variation_apply_eq_zero (f : X → E) {s : Set X} + (hs : μ.variation s = 0) : + ∫ᵛ x in s, f x ∂[B; μ] = 0 := by + by_cases h's : MeasurableSet s; swap + · simp [restrict_not_measurable μ h's] + have : (μ.restrict s).variation = 0 := by + rw [variation_restrict h's] + apply Measure.restrict_eq_zero.2 hs + have : μ.restrict s = 0 := variation_eq_zero.1 this + simpa [integral_eq_setToFun, this] using! setToFun_zero_left + +theorem setIntegral_dirac' {mX : MeasurableSpace X} [CompleteSpace G] {a : X} {v : F} + (hf : StronglyMeasurable f) {s : Set X} (hs : MeasurableSet s) [Decidable (a ∈ s)] : + ∫ᵛ x in s, f x ∂[B; VectorMeasure.dirac a v] = if a ∈ s then B (f a) v else 0 := by + rw [restrict_dirac hs] + split_ifs + · exact integral_dirac' hf + · exact integral_zero_vectorMeasure + +theorem setIntegral_dirac [MeasurableSpace X] [MeasurableSingletonClass X] [CompleteSpace G] + {a : X} {v : F} {s : Set X} (hs : MeasurableSet s) [Decidable (a ∈ s)] : + ∫ᵛ x in s, f x ∂[B; VectorMeasure.dirac a v] = if a ∈ s then B (f a) v else 0 := by + rw [restrict_dirac hs] + split_ifs + · exact integral_dirac + · exact integral_zero_vectorMeasure + +theorem integral_singleton' [CompleteSpace G] {a : X} (hf : StronglyMeasurable f) : + ∫ᵛ a in {a}, f a ∂[B; μ] = B (f a) (μ {a}) := by + simp only [restrict_singleton, integral_dirac' hf] + +theorem integral_singleton [MeasurableSingletonClass X] {a : X} [CompleteSpace G] : + ∫ᵛ a in {a}, f a ∂[B; μ] = B (f a) (μ {a}) := by + simp only [restrict_singleton, integral_dirac] + +theorem setIntegral_union_eq_left_of_ae (hs : MeasurableSet s) (ht : MeasurableSet t) + (ht_eq : ∀ᵐ x ∂μ.variation.restrict t, f x = 0) : + ∫ᵛ x in s ∪ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by + classical + rw [← integral_indicator hs, ← integral_indicator (hs.union ht)] + apply integral_congr_ae + rw [ae_restrict_iff' ht] at ht_eq + filter_upwards [ht_eq] with x hx + classical + simp only [indicator_apply, mem_union] + grind + +theorem setIntegral_union_eq_left_of_forall (hs : MeasurableSet s) (ht : MeasurableSet t) + (ht_eq : ∀ x ∈ t, f x = 0) : ∫ᵛ x in s ∪ t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by + apply setIntegral_union_eq_left_of_ae hs ht + rw [ae_restrict_iff' ht] + filter_upwards with x using ht_eq x + +theorem setIntegral_eq_of_subset_of_ae_sdiff_eq_zero (hs : MeasurableSet s) (ht : MeasurableSet t) + (hts : s ⊆ t) (h't : ∀ᵐ x ∂μ.variation.restrict (t \ s), f x = 0) : + ∫ᵛ x in t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by + rwa [← union_sdiff_cancel hts, setIntegral_union_eq_left_of_ae hs (ht.diff hs)] + +/-- If a function vanishes on `t \ s` with `s ⊆ t`, then its integrals on `s` +and `t` coincide. -/ +theorem setIntegral_eq_of_subset_of_forall_sdiff_eq_zero + (hs : MeasurableSet s) (ht : MeasurableSet t) (hts : s ⊆ t) + (h't : ∀ x ∈ t \ s, f x = 0) : ∫ᵛ x in t, f x ∂[B; μ] = ∫ᵛ x in s, f x ∂[B; μ] := by + apply setIntegral_eq_of_subset_of_ae_sdiff_eq_zero hs ht hts + apply (ae_restrict_iff' (ht.diff hs)).2 + filter_upwards with x using h't x + +/-- If a function vanishes almost everywhere on `sᶜ`, then its integral on `s` +coincides with its integral on the whole space. -/ +theorem setIntegral_eq_integral_of_ae_compl_eq_zero (hs : MeasurableSet s) + (h : ∀ᵐ x ∂μ.variation, x ∉ s → f x = 0) : + ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] := by + symm + nth_rw 1 [← setIntegral_univ] + apply setIntegral_eq_of_subset_of_ae_sdiff_eq_zero hs MeasurableSet.univ (subset_univ _) + apply (ae_restrict_iff' (MeasurableSet.univ.diff hs)).2 + filter_upwards [h] with x hx h'x using hx h'x.2 + +/-- If a function vanishes on `sᶜ`, then its integral on `s` coincides with its integral on the +whole space. -/ +theorem setIntegral_eq_integral_of_forall_compl_eq_zero (hs : MeasurableSet s) + (h : ∀ x, x ∉ s → f x = 0) : + ∫ᵛ x in s, f x ∂[B; μ] = ∫ᵛ x, f x ∂[B; μ] := + setIntegral_eq_integral_of_ae_compl_eq_zero hs (Eventually.of_forall h) + +theorem setIntegral_const [CompleteSpace G] [IsFiniteMeasure (μ.variation.restrict s)] + (c : E) : ∫ᵛ _ in s, c ∂[B; μ] = B c (μ s) := by + by_cases hs : MeasurableSet s + · have : IsFiniteMeasure (μ.restrict s).variation := by + rwa [variation_restrict hs] + rw [integral_const, restrict_apply _ hs MeasurableSet.univ, univ_inter] + · simp [setIntegral_eq_zero_of_not_measurableSet hs, μ.not_measurable hs] + +@[simp] +theorem integral_indicator_const [CompleteSpace G] + (e : E) ⦃s : Set X⦄ [IsFiniteMeasure (μ.variation.restrict s)] + (s_meas : MeasurableSet s) : + ∫ᵛ x, s.indicator (fun _ : X ↦ e) x ∂[B; μ] = B e (μ s) := by + rw [integral_indicator s_meas, ← setIntegral_const] + +theorem setIntegral_map {β : Type*} [MeasurableSpace β] + {φ : X → β} (hφ : Measurable φ) {f : β → E} {s : Set β} (hs : MeasurableSet s) + (hfm : AEStronglyMeasurable f ((μ.restrict (φ ⁻¹' s)).variation.map φ)) + (hfi' : μ.Integrable (f ∘ φ)) : + ∫ᵛ y in s, f y ∂[B; μ.map φ] = ∫ᵛ x in φ ⁻¹' s, f (φ x) ∂[B; μ] := by + rw [restrict_map μ hφ hs, integral_map hφ hfm hfi'.integrableOn] + +theorem _root_.MeasurableEmbedding.setIntegral_map_vectorMeasure {β : Type*} [MeasurableSpace β] + {φ : X → β} {f : β → E} (hφ : MeasurableEmbedding φ) {s : Set β} (hs : MeasurableSet s) : + ∫ᵛ y in s, f y ∂[B; μ.map φ] = ∫ᵛ x in φ ⁻¹' s, f (φ x) ∂[B; μ] := by + rw [restrict_map μ hφ.measurable hs, hφ.integral_map_vectorMeasure] + +theorem _root_.Topology.IsClosedEmbedding.setIntegral_map_vectorMeasure + [TopologicalSpace X] [BorelSpace X] {β : Type*} + [MeasurableSpace β] [TopologicalSpace β] [BorelSpace β] {φ : X → β} {f : β → E} {s : Set β} + (hs : MeasurableSet s) (hφ : IsClosedEmbedding φ) : + ∫ᵛ y in s, f y ∂[B; μ.map φ] = ∫ᵛ x in φ ⁻¹' s, f (φ x) ∂[B; μ] := + hφ.measurableEmbedding.setIntegral_map_vectorMeasure hs + +theorem setIntegral_map_equiv {β : Type*} [MeasurableSpace β] {e : X ≃ᵐ β} {f : β → E} {s : Set β} + (hs : MeasurableSet s) : + ∫ᵛ y in s, f y ∂[B; μ.map e] = ∫ᵛ x in e ⁻¹' s, f (e x) ∂[B; μ] := + e.measurableEmbedding.setIntegral_map_vectorMeasure hs + +theorem continuousLinearMap_apply_integral + [CompleteSpace G] [CompleteSpace H] + {C : G →L[ℝ] H} (hf : Integrable f μ.variation) : + C (∫ᵛ y, f y ∂[B; μ]) = ∫ᵛ y, f y ∂[((compL ℝ F G H C) ∘L B); μ] := by + apply hf.induction (P := fun f ↦ C (∫ᵛ y, f y ∂[B; μ]) = ∫ᵛ y, f y ∂[((compL ℝ F G H C) ∘L B); μ]) + · intro c s hs hc + have : IsFiniteMeasure (μ.variation.restrict s) := ⟨by simpa⟩ + simp [integral_indicator_const _ hs] + · intro f g _ f_int g_int hf hg + simp only [Pi.add_apply] + simp [integral_fun_add, f_int, g_int, hf, hg] + · apply isClosed_eq + · apply C.continuous.comp continuous_integral + · exact continuous_integral + · intro f g hfg _ hf + rw [← integral_congr_ae hfg, ← integral_congr_ae hfg, hf] + +theorem integral_continuousLinearMap_comp + {f : X → H} {C : H →L[ℝ] E} (hf : Integrable f μ.variation) : + ∫ᵛ y, C (f y) ∂[B; μ] = ∫ᵛ y, f y ∂[B ∘L C; μ] := by + by_cases hG : CompleteSpace G; swap + · simp [integral_of_not_completeSpace hG] + apply hf.induction (P := fun f ↦ ∫ᵛ y, C (f y) ∂[B; μ] = ∫ᵛ y, f y ∂[B ∘L C; μ]) + · intro c s hs hc + have : IsFiniteMeasure (μ.variation.restrict s) := ⟨by simpa⟩ + rw [integral_indicator_const _ hs] + have : (fun y ↦ C (s.indicator (fun x ↦ c) y)) = s.indicator (fun x ↦ C c) := by + ext; simp only [indicator]; grind + simp_rw [this] + rw [integral_indicator_const _ hs] + rfl + · intro f g _ f_int g_int hf hg + simp only [Pi.add_apply, _root_.map_add] + rw [integral_fun_add (C.integrable_comp f_int) (C.integrable_comp g_int), hf, hg, + integral_fun_add f_int g_int] + · apply isClosed_eq + · have I (f : Lp H 1 μ.variation) : ∫ᵛ x, C (f x) ∂[B; μ] = ∫ᵛ x, (C.compLp f) x ∂[B; μ] := + (integral_congr_ae (coeFn_compLp _ _)).symm + simp_rw [I] + exact continuous_integral.comp (C.compLpL 1 μ.variation).continuous + · exact continuous_integral + · intro f g hfg _ hf + have : ∀ᵐ x ∂μ.variation, C (f x) = C (g x) := by + filter_upwards [hfg] with x hx using by simp [hx] + rw [← integral_congr_ae hfg, ← integral_congr_ae this, hf] + +theorem enorm_setIntegral_le_of_enorm_le_const_ae {C : ℝ≥0∞} + (hC : ∀ᵐ x ∂μ.variation.restrict s, ‖f x‖ₑ ≤ C) : + ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ C * ‖B‖ₑ * μ.variation s := by + by_cases hs : MeasurableSet s; swap + · simp [setIntegral_eq_zero_of_not_measurableSet hs] + rw [← variation_restrict hs] at hC + apply (enorm_integral_le_of_enorm_le_const hC).trans + rw [variation_restrict hs, Measure.restrict_apply MeasurableSet.univ] + simp + +theorem enorm_setIntegral_le_of_enorm_le_const {C : ℝ≥0∞} + (hC : ∀ x ∈ s, ‖f x‖ₑ ≤ C) : + ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ C * ‖B‖ₑ * μ.variation s := by + by_cases hs : MeasurableSet s; swap + · simp [setIntegral_eq_zero_of_not_measurableSet hs] + apply enorm_setIntegral_le_of_enorm_le_const_ae + apply (ae_restrict_iff' hs).2 + filter_upwards with x using hC x + +theorem norm_setIntegral_le_of_norm_le_const_ae {C : ℝ} + [h : IsFiniteMeasure (μ.variation.restrict s)] + (hC : ∀ᵐ x ∂μ.variation.restrict s, ‖f x‖ ≤ C) : + ‖∫ᵛ x in s, f x ∂[B; μ]‖ ≤ C * ‖B‖ * μ.variation.real s := by + by_cases hs : MeasurableSet s; swap + · simp only [setIntegral_eq_zero_of_not_measurableSet hs, norm_zero] + by_cases h's : μ.variation s = 0 + · simp [Measure.real, h's] + · have : NeBot (ae (μ.variation.restrict s)) := by simpa using h's + obtain ⟨x, hx⟩ : ∃ x, ‖f x‖ ≤ C := hC.exists + have : 0 ≤ C := le_trans (norm_nonneg _) hx + positivity + rw [← variation_restrict hs] at hC h + apply (norm_integral_le_of_norm_le_const hC).trans_eq + simp [variation_restrict hs] + +theorem norm_setIntegral_le_of_norm_le_const {C : ℝ} + [h : IsFiniteMeasure (μ.variation.restrict s)] + (hC : ∀ x ∈ s, ‖f x‖ ≤ C) : + ‖∫ᵛ x in s, f x ∂[B; μ]‖ ≤ C * ‖B‖ * μ.variation.real s := by + rcases eq_empty_or_nonempty s with rfl | ⟨x, hx⟩ + · simp + by_cases hs : MeasurableSet s; swap + · simp only [setIntegral_eq_zero_of_not_measurableSet hs, norm_zero] + have : 0 ≤ C := le_trans (norm_nonneg _) (hC x hx) + positivity + apply norm_setIntegral_le_of_norm_le_const_ae + filter_upwards [ae_restrict_mem hs] with x hx using hC x hx + +theorem enorm_setIntegral_le_lintegral_enorm : + ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ ‖B‖ₑ * ∫⁻ x in s, ‖f x‖ₑ ∂μ.variation := by + grw [enorm_integral_le_lintegral_enorm, variation_restrict_le] + +theorem enorm_setIntegral_le_lintegral_enorm_transpose : + ‖∫ᵛ x in s, f x ∂[B; μ]‖ₑ ≤ ∫⁻ x in s, ‖f x‖ₑ ∂(μ.transpose B).variation := by + grw [enorm_integral_le_lintegral_enorm_transpose, transpose_restrict,variation_restrict_le] + +private theorem hasSum_setIntegral_iUnion_nat {s : ℕ → Set X} + (hm : ∀ i, MeasurableSet (s i)) (hd : Pairwise (Disjoint on s)) + (hfi : μ.IntegrableOn f (⋃ i, s i)) : + HasSum (fun n ↦ ∫ᵛ x in s n, f x ∂[B; μ]) (∫ᵛ x in ⋃ n, s n, f x ∂[B; μ]) := by + by_cases hG : CompleteSpace G; swap + · simp [integral_of_not_completeSpace hG] + have I : ∑' i, ‖B‖ₑ * ∫⁻ x in s i, ‖f x‖ₑ ∂μ.variation < ∞ := calc + ∑' i, ‖B‖ₑ * ∫⁻ x in s i, ‖f x‖ₑ ∂μ.variation + _ = ‖B‖ₑ * ∫⁻ x in (⋃ i, s i), ‖f x‖ₑ ∂μ.variation := by + rw [ENNReal.tsum_mul_left, lintegral_iUnion hm hd] + _ < ∞ := by + simp only [VectorMeasure.IntegrableOn, VectorMeasure.Integrable, + variation_restrict (MeasurableSet.iUnion hm)] at hfi + exact ENNReal.mul_lt_top (by simp) hfi.2 + have : Summable (fun n ↦ ∫ᵛ x in s n, f x ∂[B; μ]) := by + apply Summable.of_enorm (lt_of_le_of_lt _ I).ne + gcongr + exact enorm_setIntegral_le_lintegral_enorm + apply (Summable.hasSum_iff_tendsto_nat this).2 + simp_rw [tendsto_iff_edist_tendsto_0, edist_eq_enorm_sub, enorm_sub_rev] + apply tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds + (ENNReal.tendsto_sum_nat_add _ I.ne) (by positivity) (fun N ↦ ?_) + have : ⋃ n, s n = (⋃ n ∈ Finset.range N, s n) ∪ (⋃ n, s (n + N)) := by + ext x + have : (∃ i, x ∈ s (i + N)) ↔ (∃ i ≥ N, x ∈ s i) := + ⟨fun ⟨i, hi⟩ ↦ ⟨i + N, by grind⟩, fun ⟨i, hi, h'i⟩ ↦ ⟨i - N, by grind⟩⟩ + simp only [mem_iUnion, Finset.mem_range, mem_union, exists_prop, this, ge_iff_le] + grind + rw [this, setIntegral_union]; rotate_left + · simp only [Finset.mem_range, disjoint_iUnion_right, disjoint_iUnion_left] + intro i j hi + apply hd (by grind) + · apply MeasurableSet.biUnion (Finset.countable_toSet _) (fun i hi ↦ hm i) + · apply MeasurableSet.iUnion (fun i ↦ hm _) + · apply hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s]) + · apply hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s]) + rw [setIntegral_biUnion_finset]; rotate_left + · exact fun i hi ↦ hm i + · exact fun i hi j hj hij ↦ hd hij + · exact fun i hi ↦ hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s]) + simp only [add_sub_cancel_left] + apply enorm_setIntegral_le_lintegral_enorm.trans_eq + rw [lintegral_iUnion (fun i ↦ hm _), ENNReal.tsum_mul_left] + exact fun i j hij ↦ hd (by grind) + +theorem hasSum_setIntegral_iUnion {ι : Type*} [Countable ι] {s : ι → Set X} + (hm : ∀ i, MeasurableSet (s i)) (hd : Pairwise (Disjoint on s)) + (hfi : μ.IntegrableOn f (⋃ i, s i)) : + HasSum (fun n ↦ ∫ᵛ x in s n, f x ∂[B; μ]) (∫ᵛ x in ⋃ n, s n, f x ∂[B; μ]) := by + classical + rcases finite_or_infinite ι with hι | hι + · letI : Fintype ι := Fintype.ofFinite ι + have : ∫ᵛ x in ⋃ n, s n, f x ∂[B; μ] = ∑ i, ∫ᵛ x in s i, f x ∂[B; μ] := by + rw [setIntegral_iUnion_fintype hm hd (fun i ↦ ?_)] + exact hfi.mono (MeasurableSet.iUnion hm) (by simp [subset_iUnion s]) + rw [this] + apply hasSum_fintype + obtain ⟨e⟩ : Nonempty (ι ≃ ℕ) := nonempty_equiv_of_countable + rw [← e.symm.surjective.iUnion_comp, ← e.symm.hasSum_iff] + apply hasSum_setIntegral_iUnion_nat (fun i ↦ hm _) (fun i j hij ↦ hd (by simp [hij])) + rwa [e.symm.surjective.iUnion_comp] + +theorem integral_iUnion {ι : Type*} [Countable ι] {s : ι → Set X} (hm : ∀ i, MeasurableSet (s i)) + (hd : Pairwise (Disjoint on s)) (hfi : μ.IntegrableOn f (⋃ i, s i)) : + ∫ᵛ x in ⋃ n, s n, f x ∂[B; μ] = ∑' n, ∫ᵛ x in s n, f x ∂[B; μ] := + (HasSum.tsum_eq (hasSum_setIntegral_iUnion hm hd hfi)).symm + +@[simp] theorem setIntegral_toSignedMeasure {μ : Measure X} [IsFiniteMeasure μ] + {f : X → G} {s : Set X} (hs : MeasurableSet s) : + ∫ᵛ x in s, f x ∂<•μ.toSignedMeasure = ∫ x in s, f x ∂μ := by + rw [← integral_toSignedMeasure, restrict_toSignedMeasure hs] + +/-- If `f` is integrable, then `∫ᵛ x in s, f x ∂[B; μ]` is absolutely continuous in `s`: +it tends to zero as `μ.variation s` tends to zero. -/ +theorem Integrable.tendsto_setIntegral_nhds_zero {ι : Type*} + (hf : μ.Integrable f) {l : Filter ι} {s : ι → Set X} + (hs : Tendsto (μ.variation ∘ s) l (𝓝 0)) : + Tendsto (fun i ↦ ∫ᵛ x in s i, f x ∂[B; μ]) l (𝓝 0) := by + rw [tendsto_zero_iff_norm_tendsto_zero] + simp_rw [← coe_nnnorm, ← NNReal.coe_zero, NNReal.tendsto_coe, ← ENNReal.tendsto_coe, + ENNReal.coe_zero] + have : Tendsto (fun i ↦ ‖B‖ₑ * ∫⁻ (x : X) in s i, ‖f x‖ₑ ∂μ.variation) l (𝓝 (‖B‖ₑ * 0)) := + ENNReal.Tendsto.const_mul (tendsto_setLIntegral_zero (ne_of_lt hf.2) hs) (by simp) + rw [mul_zero] at this + apply tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds this (fun i ↦ zero_le) + intro i + apply enorm_integral_le_lintegral_enorm.trans + dsimp + gcongr + exact variation_restrict_le + +/-- If `F i → f` in `L1`, then `∫ᵛ x in s, F i x ∂[B; μ] → ∫ᵛ x in s, f x ∂[B; μ]`. -/ +lemma tendsto_setIntegral_of_L1 {ι} (f : X → E) + (hfi : AEStronglyMeasurable f μ.variation) {F : ι → X → E} + {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i)) + (hF : Tendsto (fun i ↦ ∫⁻ x, ‖F i x - f x‖ₑ ∂μ.variation) l (𝓝 0)) + (s : Set X) : + Tendsto (fun i ↦ ∫ᵛ x in s, F i x ∂[B; μ]) l (𝓝 (∫ᵛ x in s, f x ∂[B; μ])) := by + refine tendsto_integral_of_L1 f ?_ ?_ ?_ + · apply hfi.mono_measure + grw [variation_restrict_le, Measure.restrict_le_self] + · filter_upwards [hFi] with i hi using hi.restrict + · simp_rw [← eLpNorm_one_eq_lintegral_enorm] at hF ⊢ + apply tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds hF (fun _ ↦ zero_le) + (fun i ↦ ?_) + apply eLpNorm_mono_measure + grw [variation_restrict_le] + apply Measure.restrict_le_self + +/-- If `F i → f` in `L1`, then `∫ᵛ x in s, F i x ∂[B; μ] → ∫ᵛ x in s, f x ∂[B; μ]`. -/ +lemma tendsto_setIntegral_of_L1' {ι} (f : X → E) + (hfi : AEStronglyMeasurable f μ.variation) {F : ι → X → E} + {l : Filter ι} (hFi : ∀ᶠ i in l, μ.Integrable (F i)) + (hF : Tendsto (fun i ↦ eLpNorm (F i - f) 1 μ.variation) l (𝓝 0)) + (s : Set X) : + Tendsto (fun i ↦ ∫ᵛ x in s, F i x ∂[B; μ]) l (𝓝 (∫ᵛ x in s, f x ∂[B; μ])) := by + refine tendsto_setIntegral_of_L1 f hfi hFi ?_ s + simp_rw [eLpNorm_one_eq_lintegral_enorm, Pi.sub_apply] at hF + exact hF + +end MeasureTheory.VectorMeasure diff --git a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean index de8e791a846bee..70a8a696c203ed 100644 --- a/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean +++ b/Mathlib/MeasureTheory/VectorMeasure/Variation/Basic.lean @@ -23,6 +23,7 @@ such vector-valued measures. * `variation_zero`: `(0 : VectorMeasure X V).variation = 0`. * `variation_neg`: `(-μ).variation = μ.variation`. * `absolutelyContinuous`: `μ ≪ᵥ μ.variation`. +* `ennrealVariation_eq_self`: if `μ : VectorMeasure X ℝ≥0∞` then `μ.ennrealVariation = μ`. ## References @@ -39,6 +40,13 @@ namespace MeasureTheory.VectorMeasure variable {X V : Type*} {mX : MeasurableSpace X} +/-- The sum of a vector measure `μ` on a `Finpartition` of `Subtype MeasurableSet` equals `μ s`. -/ +lemma sum_finpartition [AddCommMonoid V] [TopologicalSpace V] [T2Space V] + (μ : VectorMeasure X V) {s : Set X} {hs : MeasurableSet s} + (P : Finpartition (⟨s, hs⟩ : Subtype MeasurableSet)) : ∑ p ∈ P.parts, μ p.val = μ s := by + rw [← μ.of_biUnion_finset (P.pairwiseDisjoint_apply (fun _ _ => rfl) rfl) (fun p _ => p.prop), + ← Finset.sup_set_eq_biUnion, P.sup_parts_apply (fun _ _ => rfl) rfl] + section Basic variable [TopologicalSpace V] [ENormedAddCommMonoid V] [T2Space V] @@ -75,6 +83,31 @@ lemma le_variation (μ : VectorMeasure X V) {s : Set X} (hs : MeasurableSet s) { simp only [sup_set_eq_biUnion, id_eq] exact hs.diff <| .biUnion (Finset.countable_toSet _) (by simp) +/-- Measure version of `preVariation.exists_Finpartition_sum_gt`. -/ +lemma exists_lt_sum_of_lt_variation (μ : VectorMeasure X V) {s : Set X} (hs : MeasurableSet s) + {a : ℝ≥0∞} (ha : a < μ.variation s) : + ∃ (P : Finset (Set X)), (∀ t ∈ P, t ⊆ s) ∧ ((P : Set (Set X)).PairwiseDisjoint id) ∧ + (∀ t ∈ P, MeasurableSet t) ∧ a < ∑ p ∈ P, ‖μ p‖ₑ := by + simp only [variation_apply, preVariation, ennrealToMeasure_apply hs, ennrealPreVariation_apply] + at ha ⊢ + obtain ⟨P, hP⟩ : ∃ P : Finpartition (⟨s, hs⟩ : Subtype MeasurableSet), + a < ∑ p ∈ P.parts, (fun x ↦ ‖μ x‖ₑ) p := + preVariation.exists_Finpartition_sum_gt (‖μ ·‖ₑ) _ ha + refine ⟨P.parts.map (Function.Embedding.subtype _), ?_, ?_, ?_, ?_⟩ + · simp only [mem_map, Function.Embedding.subtype_apply, Subtype.exists, exists_and_right, + exists_eq_right, forall_exists_index] + intro t ht h't + exact P.le h't + · intro i hi j hj hij + simp only [coe_map, Function.Embedding.subtype_apply, Set.mem_image, SetLike.mem_coe, + Subtype.exists, exists_and_right, exists_eq_right] at hi hj + rcases hi with ⟨h'i, i_mem⟩ + rcases hj with ⟨h'j, j_mem⟩ + exact (disjoint_subtype_iff (fun _ _ hs ht ↦ hs.inter ht) _).1 + (P.disjoint i_mem j_mem (by simpa using hij)) + · simp +contextual + · rwa [Finset.sum_map] + /-- Measure version of `preVariation.exists_Finpartition_sum_ge'`. -/ lemma exists_variation_le_add' (μ : VectorMeasure X V) {s : Set X} (hs : MeasurableSet s) {ε : ℝ≥0∞} (hε : 0 < ε) (hμ : μ.variation s ≠ ∞) : @@ -316,6 +349,96 @@ instance {x : X} {v : V} : IsFiniteMeasure (VectorMeasure.dirac x v).variation : apply le_trans ?_ (enorm_measure_le_variation _ _) simp [hs, Measure.real, Real.enorm_eq_ofReal] +/-- For a signed measure, the variation is realized by the norm of the measure of a single set, up +to a factor of `2` and an arbitrarily small error. -/ +lemma _root_.MeasureTheory.SignedMeasure.exists_subset_lt_enorm_apply_of_lt_variation + (μ : SignedMeasure X) {s : Set X} (hs : MeasurableSet s) + {a : ℝ≥0∞} (ha : a < μ.variation s) : + ∃ t ⊆ s, MeasurableSet t ∧ a < 2 * ‖μ t‖ₑ := by + /- One may almost realize the variation through a partition into finitely many sets. + As their measures are real numbers, we can group together those of positive measure, and + also those of negative measure. This gives two measurable sets. Among these two, the one with the + largest measure in absolute value satisfies the result. -/ + obtain ⟨P, Ps, P_disj, P_meas, hP⟩ : ∃ (P : Finset (Set X)), (∀ t ∈ P, t ⊆ s) ∧ + ((P : Set (Set X)).PairwiseDisjoint id) ∧ + (∀ t ∈ P, MeasurableSet t) ∧ a < ∑ p ∈ P, ‖μ p‖ₑ := exists_lt_sum_of_lt_variation _ hs ha + have I : (∑ p ∈ P.filter (fun p ↦ 0 ≤ μ p), ‖μ p‖ₑ) = + ‖μ (⋃ p ∈ P.filter (fun p ↦ 0 ≤ μ p), p)‖ₑ := by + simp only [Real.norm_eq_abs, enorm_eq_nnnorm, + ← ENNReal.ofNNReal_finsetSum, ENNReal.coe_inj, ← NNReal.coe_inj, + NNReal.coe_sum, coe_nnnorm, Real.norm_eq_abs] + have A : ∑ x ∈ P with 0 ≤ μ x, |μ x| = μ (⋃ x ∈ P.filter (fun x ↦ 0 ≤ μ x), x) := calc + _ = ∑ x ∈ P with 0 ≤ μ x, μ x := by + apply Finset.sum_congr rfl (fun p hp ↦ ?_) + simp only [Finset.mem_filter] at hp + simp [hp] + _ = μ (⋃ x ∈ P.filter (fun x ↦ 0 ≤ μ x), x) := by + rw [of_biUnion_finset] + · apply P_disj.subset (by grind) + · grind + rw [A, abs_of_nonneg] + rw [← A] + exact Finset.sum_nonneg (fun p hp ↦ by positivity) + have J : (∑ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), ‖μ p‖ₑ) = + ‖μ (⋃ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), p)‖ₑ := by + simp only [not_le, enorm_eq_nnnorm, ← ENNReal.ofNNReal_finsetSum, + ENNReal.coe_inj, ← NNReal.coe_inj, NNReal.coe_sum, coe_nnnorm, Real.norm_eq_abs] + have A : ∑ x ∈ P with μ x < 0, |μ x| = - μ (⋃ x ∈ P.filter (fun x ↦ μ x < 0), x) := calc + ∑ x ∈ P with μ x < 0, |μ x| + _ = ∑ x ∈ P with μ x < 0, -μ x := by + refine Finset.sum_congr rfl (fun p hp ↦ ?_) + simp only [Finset.mem_filter] at hp + simp [hp.2.le] + _ = -μ (⋃ x ∈ P.filter (fun x ↦ μ x < 0), x) := by + rw [of_biUnion_finset] + · simp + · apply P_disj.subset (by grind) + · grind + rw [A, abs_of_nonpos] + rw [← neg_nonneg, ← A] + exact Finset.sum_nonneg (fun p hp ↦ by positivity) + simp_rw [two_mul] + rw [← Finset.sum_filter_add_sum_filter_not _ (fun p ↦ 0 ≤ μ p), I, J] at hP + rcases le_total (‖μ (⋃ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), p)‖ₑ) + (‖μ (⋃ p ∈ P.filter (fun p ↦ 0 ≤ μ p), p)‖ₑ) with h | h + · refine ⟨⋃ p ∈ P.filter (fun p ↦ 0 ≤ μ p), p, ?_, ?_, ?_⟩ + · simp; grind + · exact Finset.measurableSet_biUnion _ (by grind) + · exact hP.trans_le (by gcongr) + · refine ⟨⋃ p ∈ P.filter (fun p ↦ ¬ 0 ≤ μ p), p, ?_, ?_, ?_⟩ + · simp; grind + · exact Finset.measurableSet_biUnion _ (by grind) + · exact hP.trans_le (by gcongr) + end NormedAddCommGroup +section ENNReal + +variable (μ : VectorMeasure X ℝ≥0∞) + +/-- For `μ : VectorMeasure X ℝ≥0∞` and measurable `s`, the supremum over Finpartitions of +`⟨s, hs⟩ : Subtype MeasurableSet` of the sum of `μ` over parts equals `μ s`. -/ +@[simp] +lemma iSup_sum_finpartition_parts {s : Set X} (hs : MeasurableSet s) : + ⨆ (P : Finpartition (⟨s, hs⟩ : Subtype MeasurableSet)), ∑ p ∈ P.parts, μ p.val = μ s := by + simp_rw [μ.sum_finpartition, iSup_const] + +/-- For `μ : VectorMeasure X ℝ≥0∞`, `preVariationFun μ s = μ s` for any `s`. -/ +lemma preVariationFun_apply_of_ennreal (s : Set X) : preVariationFun μ s = μ s := by + by_cases h : MeasurableSet s + · rw [preVariationFun_apply] + exact iSup_sum_finpartition_parts μ h + · rw [preVariationFun_of_not_measurableSet μ h, not_measurable μ h] + +theorem variation_eq_ennrealToMeasure : μ.variation = μ.ennrealToMeasure := by + ext _ hs + simp [preVariationFun_apply_of_ennreal, variation_apply, preVariation_apply, + ennrealPreVariation_apply, ennrealToMeasure_apply hs] + +@[simp] +theorem ennrealVariation_eq_self : μ.ennrealVariation = μ := by + simp [variation_eq_ennrealToMeasure, ennrealVariation] + +end ENNReal + end MeasureTheory.VectorMeasure diff --git a/Mathlib/MeasureTheory/VectorMeasure/Variation/Semivariation.lean b/Mathlib/MeasureTheory/VectorMeasure/Variation/Semivariation.lean new file mode 100644 index 00000000000000..93253fa2861639 --- /dev/null +++ b/Mathlib/MeasureTheory/VectorMeasure/Variation/Semivariation.lean @@ -0,0 +1,186 @@ +/- +Copyright (c) 2026 Sébastien Gouëzel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sébastien Gouëzel +-/ +module + +public import Mathlib.MeasureTheory.VectorMeasure.Variation.Basic + +import Mathlib.Analysis.Normed.Module.HahnBanach + +/-! +# The semivariation of a vector measure + +The semivariation of a vector measure is the supremum of the variations of its push-forwards +to `ℝ` through all linear forms of norm at most `1`. The interest of this notion is that, in the +reals, any set has nonnegative or nonpositive measure, so that the variation is realized by +a subset (up to a factor of at most `2`). This property is inherited by the semivariation in +general: one has the inequalities +``` +‖μ s‖ₑ ≤ μ.semivariation s ≤ 2 sup_{t ⊆ s} ‖μ t‖ₑ +``` + +The notion of semivariation can in particular be used to show that any vector measure is bounded: +there exists `C < ∞` such that `‖μ s‖ ≤ C` for all `s`. + +## Main results + +* `μ.semivariation`: the semivariation of the vector measure `μ`. +* `exists_subset_lt_enorm_apply_of_lt_semivariation`: given `s`, there exists `t ⊆ s` such that + `μ.semivariation s ≤ 2 ‖μ t‖ₑ` up to an arbitrarily small error. +* `μ.bound`: the semivariation of `univ`, in `ℝ≥0`. It is finite by definition. +* `enorm_apply_le_bound`: the inequality `‖μ s‖ₑ ≤ μ.bound`, uniformly in `s`. + +## References + +* [J. Diestel and J.J. Uhl, Vector Measures][DiestelUhl1977] + +-/ + +public section + +open scoped ENNReal Function Topology NNReal +open Set Filter + +namespace MeasureTheory.VectorMeasure + +variable {X E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] {mX : MeasurableSpace X} + {μ : VectorMeasure X E} {s t : Set X} + +/-- The semivariation of a vector measure, defined as the supremum of the variations +of the images of the vector measures under continuous linear forms of norm at most `1`. -/ +noncomputable def semivariation (μ : VectorMeasure X E) (s : Set X) : ℝ≥0∞ := + ⨆ ℓ ∈ {ℓ : StrongDual ℝ E | ‖ℓ‖ₑ ≤ 1}, (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation s + +lemma semivariation_union_le : + μ.semivariation (s ∪ t) ≤ μ.semivariation s + μ.semivariation t := by + simp only [semivariation, iSup_le_iff] + intro ℓ hℓ + apply (measure_union_le _ _).trans + gcongr <;> apply le_biSup _ hℓ + +lemma semivariation_mono (hst : s ⊆ t) : μ.semivariation s ≤ μ.semivariation t := by + simp only [semivariation, iSup_le_iff] + intro ℓ hℓ + apply (measure_mono hst).trans + apply le_biSup _ hℓ + +lemma semivariation_le_variation : μ.semivariation s ≤ μ.variation s := by + simp only [semivariation, iSup_le_iff] + intro ℓ hℓ + suffices (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation ≤ μ.variation from this s + apply variation_le_of_forall_enorm_le (fun t ht ↦ ?_) + simp only [mapRange_apply, AddMonoidHom.coe_coe] + apply le_trans ?_ (enorm_measure_le_variation _ _) + exact (ContinuousLinearMap.le_opENorm _ _).trans (mul_le_of_le_one_left (by positivity) hℓ) + +lemma enorm_apply_le_semivariation : ‖μ s‖ₑ ≤ μ.semivariation s := by + by_cases hs : MeasurableSet s; swap + · simp [not_measurable, hs] + obtain ⟨ℓ, ℓ_norm, hℓ⟩ : ∃ ℓ : StrongDual ℝ E, ‖ℓ‖ ≤ 1 ∧ ℓ (μ s) = ‖μ s‖ := + exists_dual_vector'' _ _ + have h'ℓ : ℓ ∈ {ℓ : StrongDual ℝ E | ‖ℓ‖ₑ ≤ 1} := by + simp [enorm_eq_nnnorm, ← NNReal.coe_le_one, ℓ_norm] + calc ‖μ s‖ₑ + _ = ‖(μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous) s‖ₑ := by simp [← ofReal_norm, hℓ] + _ ≤ (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation s := enorm_measure_le_variation _ _ + _ ≤ μ.semivariation s := by apply le_biSup _ h'ℓ + +lemma enorm_apply_le_semivariation_of_subset (hst : s ⊆ t) : + ‖μ s‖ₑ ≤ μ.semivariation t := + enorm_apply_le_semivariation.trans (semivariation_mono hst) + +lemma exists_subset_lt_enorm_apply_of_lt_semivariation (hs : MeasurableSet s) + {a : ℝ≥0∞} (ha : a < μ.semivariation s) : + ∃ t ⊆ s, MeasurableSet t ∧ a < 2 * ‖μ t‖ₑ := by + obtain ⟨ℓ, hℓ, h'ℓ⟩ : ∃ ℓ ∈ {ℓ : StrongDual ℝ E | ‖ℓ‖ₑ ≤ 1}, + a < (μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous).variation s := lt_biSup_iff.1 ha + obtain ⟨t, ts, t_meas, ht⟩ : + ∃ t ⊆ s, MeasurableSet t ∧ a < 2 * ‖μ.mapRange (ℓ : E →+ ℝ) ℓ.continuous t‖ₑ := + SignedMeasure.exists_subset_lt_enorm_apply_of_lt_variation _ hs h'ℓ + refine ⟨t, ts, t_meas, ht.trans_le ?_⟩ + gcongr + exact (ContinuousLinearMap.le_opENorm _ _).trans (mul_le_of_le_one_left (by positivity) hℓ) + +private lemma exists_one_le_enorm_apply_of_semivariation_eq_top + (hs : MeasurableSet s) (h's : μ.semivariation s = ∞) : + ∃ t, MeasurableSet t ∧ t ⊆ s ∧ μ.semivariation t = ∞ ∧ 1 ≤ ‖μ (s \ t)‖ₑ := by + obtain ⟨t, ts, t_meas, ht⟩ : ∃ t ⊆ s, MeasurableSet t ∧ 2 * ‖μ s‖ₑ + 2 < 2 * ‖μ t‖ₑ := by + apply exists_subset_lt_enorm_apply_of_lt_semivariation hs + rw [h's] + finiteness + have h't : 1 + ‖μ s‖ₑ ≤ ‖μ t‖ₑ := by + apply (ENNReal.mul_le_mul_iff_right (a := 2) (by simp) (by simp)).1 + rw [mul_add, add_comm, mul_one] + exact ht.le + have I : ∞ ≤ μ.semivariation t + μ.semivariation (s \ t) := by + rw [← h's] + apply le_trans (semivariation_mono (by simp)) semivariation_union_le + simp only [top_le_iff, ENNReal.add_eq_top] at I + rcases I with hI | hI + · refine ⟨t, t_meas, ts, hI, ?_⟩ + have : 1 + ‖μ s‖ₑ ≤ ‖μ (s \ t)‖ₑ + ‖μ s‖ₑ := by + apply h't.trans + have : μ t = μ s - μ (s \ t) := by rw [← of_add_of_sdiff t_meas hs ts]; abel + rw [this, add_comm] + exact enorm_sub_le + rwa [ENNReal.add_le_add_iff_right (by simp)] at this + · refine ⟨s \ t, hs.diff t_meas, sdiff_subset, hI, ?_⟩ + simp only [_root_.sdiff_sdiff_right_self, le_eq_subset, ts, inf_of_le_right] + exact le_trans (by simp) h't + +private lemma semivariation_univ_lt_top : μ.semivariation univ < ∞ := by + apply Ne.lt_top (fun h ↦ ?_) + have A (s : Set X) (hs : MeasurableSet s) (h's : μ.semivariation s = ∞) : + ∃ t, MeasurableSet t ∧ t ⊆ s ∧ μ.semivariation t = ∞ ∧ 1 ≤ ‖μ (s \ t)‖ₑ := + exists_one_le_enorm_apply_of_semivariation_eq_top hs h's + choose! t t_meas t_subs t_var ht using A + let s n := t^[n] univ + have hs n : MeasurableSet (s n) ∧ μ.semivariation (s n) = ∞ := by + induction n with + | zero => simp [s, h] + | succ n ih => + simp only [Function.iterate_succ', Function.comp_apply, s] + exact ⟨t_meas _ ih.1 ih.2, t_var _ ih.1 ih.2⟩ + let u n := s n \ s (n + 1) + have hu n : 1 ≤ ‖μ (u n)‖ₑ := by + simp only [Function.iterate_succ', Function.comp_apply, u, s] + exact ht _ (hs n).1 (hs n).2 + have s_anti : Antitone s := by + apply antitone_nat_of_succ_le (fun n ↦ ?_) + simp only [Function.iterate_succ', Function.comp_apply, s] + apply t_subs _ (hs n).1 (hs n).2 + have u_disj : Pairwise (Disjoint on u) := by + apply (pairwise_disjoint_on _).2 (fun m n hmn ↦ ?_) + have : Disjoint (u m) (s (m + 1)) := by simp [u, disjoint_sdiff_left] + apply this.mono_right + simp only [sdiff_le_iff, sup_eq_union, le_eq_subset, u] + exact Subset.trans (s_anti (by grind)) subset_union_right + have : HasSum (fun i => μ (u i)) (μ (⋃ i, u i)) := + hasSum_of_disjoint_iUnion (fun n ↦ (hs n).1.diff (hs (n + 1)).1) u_disj + have : Tendsto (fun x ↦ ‖μ (u x)‖ₑ) atTop (𝓝 0) := + tendsto_zero_iff_enorm_tendsto_zero.1 this.summable.tendsto_atTop_zero + obtain ⟨n, hn⟩ : ∃ n, ‖μ (u n)‖ₑ < 1 := ((tendsto_order.1 this).2 _ zero_lt_one).exists + order [hu n] + +variable (μ) in +/-- A constant bounding the norm of `μ s` for any set `s`. -/ +protected noncomputable def bound : ℝ≥0 := (μ.semivariation univ).toNNReal + +lemma semivariation_apply_le_bound : μ.semivariation s ≤ μ.bound := by + apply (semivariation_mono (subset_univ _)).trans_eq + simp only [VectorMeasure.bound] + rw [ENNReal.coe_toNNReal semivariation_univ_lt_top.ne] + +lemma enorm_apply_le_bound : ‖μ s‖ₑ ≤ μ.bound := + (enorm_apply_le_semivariation).trans semivariation_apply_le_bound + +lemma nnnorm_apply_le_bound : ‖μ s‖₊ ≤ μ.bound := by + rw [← ENNReal.coe_le_coe, ← enorm_eq_nnnorm] + exact enorm_apply_le_bound + +lemma norm_apply_le_bound : ‖μ s‖ ≤ μ.bound := by + simpa [← coe_nnnorm] using nnnorm_apply_le_bound + +end MeasureTheory.VectorMeasure diff --git a/Mathlib/MeasureTheory/VectorMeasure/WithDensityVec.lean b/Mathlib/MeasureTheory/VectorMeasure/WithDensityVec.lean new file mode 100644 index 00000000000000..4b87371e6e8109 --- /dev/null +++ b/Mathlib/MeasureTheory/VectorMeasure/WithDensityVec.lean @@ -0,0 +1,397 @@ +/- +Copyright (c) 2026 Sébastien Gouëzel. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Sébastien Gouëzel +-/ +module + +public import Mathlib.MeasureTheory.VectorMeasure.SetIntegral +public import Mathlib.MeasureTheory.VectorMeasure.WithDensity + +/-! +# Vector measure with density with respect to a vector measure + +Given a vector measure `μ`, a function `f` and a pairing `B`, we define the vector measure +with density `f` and pairing `B`, denoted `μ.withDensity f B`. It associates to a +measurable set the mass `∫ᵛ x in s, f x ∂[B; μ]`. + +This file implements the basic property of this notion. Notably, we show in `variation_withDensity` +that the variation of the vector measure `μ.withDensity f B` is the positive measure with +density `‖f‖` with respect to the positive measure `μ.variation`. +-/ + +open Set Filter +open scoped Topology ENNReal + +@[expose] public section + +namespace MeasureTheory.VectorMeasure + +local infixr:25 " →ₛ " => SimpleFunc + +variable {X E F G : Type*} {mX : MeasurableSpace X} + [NormedAddCommGroup E] [NormedSpace ℝ E] + [NormedAddCommGroup F] [NormedSpace ℝ F] + [NormedAddCommGroup G] [NormedSpace ℝ G] + {μ : VectorMeasure X F} {f g : X → E} {B : E →L[ℝ] F →L[ℝ] G} {s : Set X} + +open scoped Classical in +/-- The vector measure with density `f` with respect to a vector measure `μ`, associating to a +measurable set the mass `∫ᵛ x in s, f x ∂[B; μ]`. +If `f` is not integrable, we use the junk value `0`. -/ +noncomputable def withDensity (μ : VectorMeasure X F) (f : X → E) (B : E →L[ℝ] F →L[ℝ] G) : + VectorMeasure X G := + if h : μ.Integrable f then + { measureOf' s := ∫ᵛ x in s, f x ∂[B; μ] + empty' := by simp + not_measurable' s hs := setIntegral_eq_zero_of_not_measurableSet hs + m_iUnion' s s_meas s_disj := hasSum_setIntegral_iUnion s_meas s_disj h.integrableOn } + else 0 + +lemma withDensity_apply (hf : μ.Integrable f) : + μ.withDensity f B s = ∫ᵛ x in s, f x ∂[B; μ] := by + simp [withDensity, hf] + +lemma withDensity_apply_univ : μ.withDensity f B univ = ∫ᵛ x, f x ∂[B; μ] := by + by_cases hf : μ.Integrable f + · simp [withDensity_apply hf] + · simp [withDensity, hf, integral_undef] + +@[simp] +lemma withDensity_zero_vectorMeasure : (0 : VectorMeasure X F).withDensity f B = 0 := by + ext s hs + simp [withDensity_apply] + +@[to_fun (attr := simp) withDensity_fun_zero] +lemma withDensity_zero : μ.withDensity 0 B = 0 := by + ext s hs + simp [withDensity_apply] + +lemma withDensity_congr (h : f =ᵐ[μ.variation] g) : + μ.withDensity f B = μ.withDensity g B := by + by_cases hf : μ.Integrable f + · simp only [withDensity, hf, ↓reduceDIte, Integrable.congr hf h, mk.injEq] + ext s + apply setIntegral_congr_ae + filter_upwards [h] with x hx xs using hx + · have : ¬(μ.Integrable g) := by simpa [← integrable_congr h] using hf + simp [withDensity, hf, this] + +lemma restrict_withDensity (hf : μ.Integrable f) : + (μ.withDensity f B).restrict s = (μ.restrict s).withDensity f B := by + by_cases hs : MeasurableSet s; swap + · simp [restrict_not_measurable _ hs] + · ext t ht + simp only [hs, ht, restrict_apply] + rw [withDensity_apply hf, withDensity_apply hf.restrict, restrict_restrict _ ht hs] + +lemma variation_WithDensity_le : + (μ.withDensity f B).variation ≤ (μ.transpose B).variation.withDensity (fun x ↦ ‖f x‖ₑ) := by + by_cases hf : μ.Integrable f + · apply variation_le_of_forall_enorm_le (fun s hs ↦ ?_) + rw [withDensity_apply hf, MeasureTheory.withDensity_apply _ hs] + apply enorm_setIntegral_le_lintegral_enorm_transpose + · simp [withDensity, hf, Measure.zero_le ] + +/-- If `‖B x y‖ = ‖B · y‖ * ‖x‖` for all `x, y`, then the variation of a vector measure with +density `f` wrt `μ` is the measure with density `‖f‖ₑ` with respect to the variation of `μ`. + +The condition on `B` is necessary: for a counterexample without it, let `B` be the scalar +product in `ℝ²` and `f x` everywhere horizontal and `μ s` everywhere vertical. +Then `μ.withDensity f B = 0` so its variation is zero, while the integral of `‖f‖ₑ` is not. + +See also `variation_withDensity` under the very common condition `‖B x y‖ = ‖x‖ ‖y‖`. +-/ +lemma variation_withDensity' [CompleteSpace G] + (hf : μ.Integrable f) (hB : ∀ x y, ‖B x y‖₊ = ‖B.flip y‖₊ * ‖x‖₊) : + (μ.withDensity f B).variation = (μ.transpose B).variation.withDensity (fun x ↦ ‖f x‖ₑ) := by + apply le_antisymm variation_WithDensity_le + apply Measure.le_iff.2 (fun s hs ↦ ?_) + /- For the nontrivial direction, we have to show that for each measurable set `s`, + `∫⁻ (a : X) in s, ‖f a‖ₑ ∂(μ.transpose B).variation ≤ (μ.withDensity f B).variation s`. + As the variation is a supremum over finite partitions, we need to exhibit a partition. For this, + we approximate `f` by a simple function `g`. Then the left term is approximately + `∑ i, ‖g i‖ₑ * (μ.transpose B).variation (g ⁻¹' {i})` (where everything is intersected with `s`). + By definition, the variation of `g ⁻¹' {i}` is close to a sum `∑ j, ‖(μ.transpose B) Pᵢⱼ‖ₑ` over + a partition `Pᵢⱼ` of `g ⁻¹' {i}`. Putting all these together, one gets the desired + partition of `s`, for which `∫⁻ a in s, ‖f a‖ₑ ∂(μ.transpose B).variation` is close to + `∑ i j, ‖∫ x in Pᵢⱼ, f x ∂[B; μ]‖ₑ`, i.e., `∑ i j, ‖(μ.withDensity f B) Pᵢⱼ‖ₑ`. The latter sum + is bounded by `(μ.withDensity f B).variation s` as desired. -/ + rw [MeasureTheory.withDensity_apply _ hs] + apply ENNReal.le_of_forall_pos_le_add + rintro ε εpos - + let δ := ε / 3 + have δpos : 0 < δ := div_pos εpos (by norm_num) + -- first step: approximate `f` by a simple function `g`. + obtain ⟨g, hg, gmem⟩ : ∃ (g : X →ₛ E), eLpNorm (f - ⇑g) 1 (μ.transpose B).variation ≤ δ + ∧ MemLp (⇑g) 1 μ.variation := by + obtain ⟨ρ, ρpos, hδ⟩ : ∃ ρ > 0, ‖B‖₊ * ρ ≤ δ := by + rcases eq_or_ne (‖B‖₊) 0 with hB | hB + · exact ⟨1, zero_lt_one, by simp [hB]⟩ + · refine ⟨‖B‖₊ ⁻¹ * δ, by positivity, ?_⟩ + rw [← mul_assoc] + apply mul_le_of_le_one_left (by positivity) mul_inv_le_one + obtain ⟨g, h'g, gmem⟩ : ∃ (g : X →ₛ E), eLpNorm (f - ⇑g) 1 μ.variation < ρ + ∧ MemLp (⇑g) 1 μ.variation := + (memLp_one_iff_integrable.2 hf).exists_simpleFunc_eLpNorm_sub_lt (by simp) + (by simpa using ρpos.ne') + refine ⟨g, ?_, gmem⟩ + grw [variation_transpose_le] + rw [eLpNorm_smul_measure_of_ne_top' (by simp)] + grw [h'g.le] + simp only [ENNReal.toReal_one, inv_one, NNReal.rpow_one, ENNReal.smul_def, smul_eq_mul] + exact_mod_cast hδ + -- the integral of `‖f‖ₑ` is approximated up to `δ` by that of `‖g‖ₑ`. + have I1 : ∫⁻ a in s, ‖f a‖ₑ ∂(μ.transpose B).variation + ≤ ∫⁻ a in s, ‖g a‖ₑ ∂(μ.transpose B).variation + δ := calc + _ ≤ ∫⁻ a in s, ‖f a - g a‖ₑ + ‖g a‖ₑ ∂(μ.transpose B).variation := by + gcongr with a + nth_rw 1 [show f a = (f a - g a) + g a by abel] + exact enorm_add_le (f a - g a) (g a) + _ = ∫⁻ a in s, ‖g a‖ₑ ∂(μ.transpose B).variation + + ∫⁻ a in s, ‖f a - g a‖ₑ ∂(μ.transpose B).variation := by + rw [lintegral_add_right, add_comm] + exact g.stronglyMeasurable.enorm + _ ≤ ∫⁻ a in s, ‖g a‖ₑ ∂(μ.transpose B).variation + + ∫⁻ a, ‖f a - g a‖ₑ ∂(μ.transpose B).variation := by + gcongr + exact Measure.restrict_le_self + _ ≤ ∫⁻ a in s, ‖g a‖ₑ ∂(μ.transpose B).variation + δ := by + rw [eLpNorm_one_eq_lintegral_enorm] at hg + gcongr + exact hg + -- the integral of `‖g‖ₑ` can be rewritten as a weighted sum of measures, as `g` is a simple + -- function. + have I2 : ∫⁻ a in s, ‖g a‖ₑ ∂(μ.transpose B).variation = + ∑ i ∈ g.range, ‖i‖ₑ * ((μ.transpose B).restrict s).variation (g ⁻¹' {i}) := calc + _ = (g.map (‖·‖ₑ)).lintegral ((μ.transpose B).variation.restrict s) := + SimpleFunc.lintegral_eq_lintegral _ _ + _ = ∑ i ∈ g.range, ‖i‖ₑ * (μ.transpose B).variation.restrict s (g ⁻¹' {i}) := + SimpleFunc.map_lintegral _ _ + _ = ∑ i ∈ g.range, ‖i‖ₑ * ((μ.transpose B).restrict s).variation (g ⁻¹' {i}) := by + simp_rw [variation_restrict hs] + -- For each `i`, choose a partition `P i` of `g ⁻¹' {i}` such that the sum of the enorms + -- of their measures approximates well enough the variation, by definition of the variation. + obtain ⟨ρ,ρpos, hρ⟩ : ∃ ρ > 0, ∑ i ∈ g.range, ‖i‖ₑ * ρ ≤ δ := by + refine ⟨δ * (∑ i ∈ g.range, ‖i‖ₑ)⁻¹, by simp [δpos], ?_⟩ + grw [← Finset.sum_mul, mul_comm (δ : ℝ≥0∞), ← mul_assoc, ENNReal.mul_inv_le_one, one_mul] + have C i : ∃ (P : Finset (Set X)), (∀ t ∈ P, t ⊆ g ⁻¹' {i}) + ∧ ((P : Set (Set X)).PairwiseDisjoint id) ∧ + (∀ t ∈ P, MeasurableSet t) ∧ + ‖i‖ₑ * ((μ.transpose B).restrict s).variation (g ⁻¹' {i}) ≤ + ‖i‖ₑ * (∑ p ∈ P, ‖(μ.transpose B).restrict s p‖ₑ + ρ) := by + rcases eq_or_ne i 0 with rfl | hi + · exact ⟨∅, by simp⟩ + suffices ∃ (P : Finset (Set X)), (∀ t ∈ P, t ⊆ g ⁻¹' {i}) + ∧ ((P : Set (Set X)).PairwiseDisjoint id) ∧ (∀ t ∈ P, MeasurableSet t) ∧ + ((μ.transpose B).restrict s).variation (g ⁻¹' {i}) ≤ + (∑ p ∈ P, ‖(μ.transpose B).restrict s p‖ₑ + ρ) by + obtain ⟨P, hP, h'P, h''P, h'''P⟩ := this + exact ⟨P, hP, h'P, h''P, by gcongr⟩ + apply exists_variation_le_add' _ (g.measurableSet_fiber i) ρpos + rw [variation_restrict hs] + have : MemLp (⇑g) 1 (μ.transpose B).variation := + gmem.of_measure_le_smul (c := ‖B‖₊) (by simp) (variation_transpose_le _ _) + exact (g.integrable_iff.1 (memLp_one_iff_integrable.1 this).restrict i hi).ne + choose P Pg Pdisj Pmeas hP using C + -- rewrite everything in terms of the global partition made by putting together the `Pᵢ`, + -- and register that the resulting error is bounded by `δ`. + have I3 : ∑ i ∈ g.range, ‖i‖ₑ * ((μ.transpose B).restrict s).variation (g ⁻¹' {i}) ≤ + ∑ i ∈ g.range.sigma P, ‖i.1‖ₑ * ‖(μ.transpose B).restrict s i.2‖ₑ + δ := calc + ∑ i ∈ g.range, ‖i‖ₑ * ((μ.transpose B).restrict s).variation (g ⁻¹' {i}) + _ ≤ ∑ i ∈ g.range, ‖i‖ₑ * ((∑ p ∈ P i, ‖(μ.transpose B).restrict s p‖ₑ) + ρ) := by + gcongr 1 with i hi + exact hP i + _ ≤ ∑ i ∈ g.range, ∑ p ∈ P i, ‖i‖ₑ * ‖(μ.transpose B).restrict s p‖ₑ + δ := by + simp_rw [mul_add, Finset.sum_add_distrib, Finset.mul_sum] + gcongr + _ = ∑ i ∈ g.range.sigma P, ‖i.1‖ₑ * ‖(μ.transpose B).restrict s i.2‖ₑ + δ := by + rw [Finset.sum_sigma'] + -- in the above sum, replace the values of `g` by `f`, as these two functions are close + -- in `L^1` norm. + have I4 : ∑ i ∈ g.range.sigma P, ‖i.1‖ₑ * ‖(μ.transpose B).restrict s i.2‖ₑ + ≤ ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ + δ := calc + ∑ i ∈ g.range.sigma P, ‖i.1‖ₑ * ‖(μ.transpose B).restrict s i.2‖ₑ + _ = ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, i.1 ∂[B; μ.restrict s]‖ₑ := by + congr! with ⟨i, p⟩ hi + rcases eq_or_ne i 0 with rfl | h'i + · simp + simp only [Finset.mem_sigma] at hi + have pmeas : MeasurableSet p := Pmeas i _ hi.2 + have : IsFiniteMeasure ((μ.restrict s).variation.restrict p) := by + constructor + rw [variation_restrict hs, Measure.restrict_restrict pmeas, + MeasureTheory.Measure.restrict_apply_univ] + apply lt_of_le_of_lt ?_ (g.integrable_iff.1 (memLp_one_iff_integrable.1 gmem) i h'i) + exact measure_mono (inter_subset_left.trans (Pg i _ hi.2)) + rw [setIntegral_const, restrict_apply _ hs pmeas, restrict_apply _ hs pmeas] + simp [transpose, hB, enorm_eq_nnnorm, mul_comm] + _ = ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, g x ∂[B; μ.restrict s]‖ₑ := by + congr! 2 with ⟨i, p⟩ hi + simp only [Finset.mem_sigma] at hi + apply setIntegral_congr_ae + filter_upwards with x hx using (Pg i _ hi.2 hx).symm + _ = ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, (g x - f x) + f x ∂[B; μ.restrict s]‖ₑ := by simp + _ = ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, (g x - f x) ∂[B; μ.restrict s] + + ∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ := by + congr! with i hi + rw [integral_fun_add] + · apply Integrable.restrict + apply Integrable.restrict + apply Integrable.sub (memLp_one_iff_integrable.1 gmem) hf + · apply hf.restrict.restrict + _ ≤ ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, (g x - f x) ∂[B; μ.restrict s]‖ₑ + + ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ := by + rw [← Finset.sum_add_distrib] + gcongr with i hi + apply enorm_add_le + _ ≤ ∑ i ∈ g.range.sigma P, ∫⁻ x in i.2, ‖g x - f x‖ₑ ∂(μ.transpose B).variation + + ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ := by + gcongr with i hi + grw [enorm_setIntegral_le_lintegral_enorm_transpose] + apply lintegral_mono' _ le_rfl + apply Measure.restrict_mono le_rfl + rw [transpose_restrict, variation_restrict hs] + apply Measure.restrict_le_self + _ = ∫⁻ x in (⋃ i ∈ g.range.sigma P, i.2), ‖g x - f x‖ₑ ∂(μ.transpose B).variation + + ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ := by + rw [lintegral_biUnion_finset] + · rintro ⟨i, p⟩ hi ⟨j, q⟩ hj hijpq + simp only [Finset.coe_sigma, SimpleFunc.coe_range, mem_sigma_iff, mem_range, + SetLike.mem_coe] at hi hj + rcases eq_or_ne i j with rfl | hij + · simp only [ne_eq, Sigma.mk.injEq, heq_eq_eq, true_and] at hijpq + exact Pdisj i hi.2 hj.2 hijpq + · have : Disjoint (g ⁻¹' {i}) (g ⁻¹' {j}) := by grind + exact this.mono (Pg i p hi.2) (Pg j q hj.2) + · rintro ⟨i, p⟩ hip + simp only [Finset.mem_sigma, SimpleFunc.mem_range, mem_range] at hip + exact Pmeas i p hip.2 + _ ≤ ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ + + ∫⁻ x, ‖g x - f x‖ₑ ∂(μ.transpose B).variation := by + rw [add_comm] + gcongr + apply Measure.restrict_le_self + _ ≤ ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ + δ := by + gcongr + simp_rw [enorm_sub_rev, ← eLpNorm_one_eq_lintegral_enorm] + exact hg + -- register that the sum of the enorms of the integrals of `f` over the pieces `Pᵢⱼ` of the + -- partition is bounded by the variation of `μ.withDensity f B`, by definition of the variation. + have I5 : ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ + ≤ (μ.withDensity f B).variation s := by + let Q : Finset (Set X) := (g.range.sigma P).image (fun p ↦ p.2 ∩ s) + calc ∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ + _ = ∑ j ∈ Q, ‖∫ᵛ x in j, f x ∂[B; μ]‖ₑ := by + simp only [Q] + rw [Finset.sum_image_of_pairwise_eq_zero]; swap + · rintro ⟨i, p⟩ hi ⟨j, q⟩ hj hijpq h'ij + simp only [Finset.coe_sigma, SimpleFunc.coe_range, mem_sigma_iff, mem_range, + SetLike.mem_coe] at hi hj + suffices H : Disjoint p q by + have : Disjoint (p ∩ s) (q ∩ s) := H.mono inter_subset_left inter_subset_left + rw [← h'ij, disjoint_self] at this + simp [this] + rcases eq_or_ne i j with rfl | hij + · simp only [ne_eq, Sigma.mk.injEq, heq_eq_eq, true_and] at hijpq + exact Pdisj i hi.2 hj.2 hijpq + · have : Disjoint (g ⁻¹' {i}) (g ⁻¹' {j}) := by grind + exact this.mono (Pg i p hi.2) (Pg j q hj.2) + apply Finset.sum_congr rfl + rintro ⟨i, p⟩ hi + simp only [Finset.mem_sigma, SimpleFunc.mem_range, mem_range] at hi + rw [restrict_restrict _ (Pmeas i p hi.2) hs] + _ = ∑ j ∈ Q, ‖μ.withDensity f B j‖ₑ := + Finset.sum_congr rfl (fun t ht ↦ by rw [withDensity_apply hf]) + _ ≤ (μ.withDensity f B).variation s := by + apply le_variation _ hs + · intro t ht + simp only [Finset.mem_image, Finset.mem_sigma, SimpleFunc.mem_range, mem_range, + Sigma.exists, ↓existsAndEq, true_and, exists_and_right, Q] at ht + rcases ht with ⟨p, -, rfl⟩ + exact inter_subset_right + · intro t ht u hu htu + simp only [Finset.coe_image, Finset.coe_sigma, SimpleFunc.coe_range, mem_image, + mem_sigma_iff, mem_range, SetLike.mem_coe, Sigma.exists, ↓existsAndEq, true_and, + exists_and_right, Q] at ht hu + rcases ht with ⟨p, ⟨i, hi⟩, rfl⟩ + rcases hu with ⟨q, ⟨j, hj⟩, rfl⟩ + have hpq : p ≠ q := by grind only + suffices H : Disjoint p q from H.mono inter_subset_left inter_subset_left + rcases eq_or_ne (g i) (g j) with hij | hij + · rw [← hij] at hj + exact Pdisj (g i) hi hj hpq + · have : Disjoint (g ⁻¹' {g i}) (g ⁻¹' {g j}) := by grind + exact this.mono (Pg (g i) p hi) (Pg (g j) q hj) + -- finally, put together the above inequalities, and argue that the overall error `3δ` is + -- bounded by `ε` by design. + calc ∫⁻ (a : X) in s, ‖f a‖ₑ ∂(μ.transpose B).variation + _ ≤ ∫⁻ a in s, ‖g a‖ₑ ∂(μ.transpose B).variation + δ := I1 + _ = ∑ i ∈ g.range, ‖i‖ₑ * ((μ.transpose B).restrict s).variation (g ⁻¹' {i}) + δ := by rw [I2] + _ ≤ (∑ i ∈ g.range.sigma P, ‖i.1‖ₑ * ‖(μ.transpose B).restrict s i.2‖ₑ + δ) + δ := by gcongr + _ ≤ ((∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ + δ) + δ) + δ := by gcongr + _ = (∑ i ∈ g.range.sigma P, ‖∫ᵛ x in i.2, f x ∂[B; μ.restrict s]‖ₑ) + 3 * δ := by ring + _ ≤ (μ.withDensity f B).variation s + 3 * δ := by gcongr + _ ≤ (μ.withDensity f B).variation s + ε := by + simp only [ne_eq, OfNat.ofNat_ne_zero, not_false_eq_true, ENNReal.coe_div, ENNReal.coe_ofNat, δ] + rw [ENNReal.mul_div_cancel (by simp) (by simp)] + +/-- If `‖B x y‖ = ‖x‖ * ‖y‖` for all `x, y`, then the variation of a vector measure with +density `f` wrt `μ` is the measure with density `‖f‖ₑ` with respect to the variation of `μ`. + +The condition on `B` is necessary: for a counterexample without it, let `B` be the scalar +product in `ℝ²` and `f x` everywhere horizontal and `μ s` everywhere vertical. +Then `μ.withDensity f B = 0` so its variation is zero, while the integral of `‖f‖ₑ` is not. +-/ +lemma variation_withDensity [CompleteSpace G] + (hf : μ.Integrable f) (hB : ∀ x y, ‖B x y‖₊ = ‖x‖₊ * ‖y‖₊) : + (μ.withDensity f B).variation = (μ.transpose B).variation.withDensity (fun x ↦ ‖f x‖ₑ) := by + apply variation_withDensity' hf (fun x y ↦ ?_) + refine le_antisymm (ContinuousLinearMap.le_opNorm (B.flip y) x) ?_ + rw [hB, mul_comm] + gcongr + apply ContinuousLinearMap.opNNNorm_le_bound + simp [hB, mul_comm] + +/-- The variation of a vecture measure with density `f` with respect to a positive measure `μ` +is the measure with density `‖f‖ₑ` with respect to `μ`. -/ +lemma _root_.MeasureTheory.Measure.variation_withDensityᵥ [CompleteSpace E] + {μ : Measure X} {f : X → E} (hf : Integrable f μ) : + (μ.withDensityᵥ f).variation = μ.withDensity (fun x ↦ ‖f x‖ₑ) := by + /- We deduce this statement from the statement `variation_withDensity` for vector measures + with density. For this, we write `μ.withDensityᵥ f` as the vector measure with density `f / ‖f‖` + with respect to the measure `μ.withDensity ‖f‖` interpreted as a signed measure. -/ + rcases subsingleton_or_nontrivial E with hE | hE + · simp [show f = 0 from Subsingleton.elim _ _] + have : IsFiniteMeasure (μ.withDensity fun x ↦ ‖f x‖ₑ) := ⟨by simpa using! hf.2⟩ + have I : (μ.withDensity fun x ↦ ‖f x‖ₑ).toSignedMeasure.Integrable (fun x ↦ ‖f x‖⁻¹ • f x) := by + simp only [VectorMeasure.Integrable, Measure.variation_toSignedMeasure] + apply Integrable.of_bound (C := 1) + · apply AEStronglyMeasurable.mono_ac (withDensity_absolutelyContinuous _ _) + exact hf.aestronglyMeasurable.norm.inv₀.smul hf.aestronglyMeasurable + · filter_upwards with x using by simp [norm_smul, inv_mul_le_one] + have : μ.withDensityᵥ f = (μ.withDensity (‖f ·‖ₑ)).toSignedMeasure.withDensity + (fun x ↦ ‖f x‖⁻¹ • f x) (ContinuousLinearMap.lsmul ℝ ℝ).flip := by + ext s hs + rw [withDensityᵥ_apply hf hs, withDensity_apply I, setIntegral_toSignedMeasure hs, + setIntegral_withDensity_eq_setIntegral_toReal_smul₀ _ _ _ hs]; rotate_left + · exact hf.aestronglyMeasurable.restrict.enorm + · filter_upwards with x using by simp + congr with x + rcases eq_or_ne (f x) 0 with hx | hx + · simp [hx] + simp only [toReal_enorm, smul_smul] + rw [mul_inv_cancel₀, one_smul] + simpa using hx + rw [this, variation_withDensity I (by simp [nnnorm_smul, mul_comm]), + variation_transpose_eq _ _ (by simp [nnnorm_smul, mul_comm]), Measure.variation_toSignedMeasure, + ← withDensity_mul₀ hf.aestronglyMeasurable.enorm]; swap + · exact (hf.aestronglyMeasurable.norm.inv₀.smul hf.aestronglyMeasurable).enorm + congr with x + rcases eq_or_ne (f x) 0 with hx | hx + · simp [hx] + have h'x : ‖f x‖ ≠ 0 := by simp [hx] + simp only [enorm_smul, Pi.mul_apply, ne_eq, h'x, not_false_eq_true, enorm_inv, enorm_norm] + rw [ENNReal.inv_mul_cancel (by simpa using hx) (by simp), mul_one] + +end MeasureTheory.VectorMeasure diff --git a/Mathlib/ModelTheory/Basic.lean b/Mathlib/ModelTheory/Basic.lean index 461a3c31318c83..c1a889021df466 100644 --- a/Mathlib/ModelTheory/Basic.lean +++ b/Mathlib/ModelTheory/Basic.lean @@ -52,9 +52,9 @@ namespace FirstOrder -- intended to be used with explicit universe parameters +set_option linter.checkUnivs false in /-- A first-order language consists of a type of functions of every natural-number arity and a type of relations of every natural-number arity. -/ -@[nolint checkUnivs] structure Language where /-- For every arity, a `Type u` of functions of that arity -/ Functions : ℕ → Type u diff --git a/Mathlib/ModelTheory/Encoding.lean b/Mathlib/ModelTheory/Encoding.lean index 13dcf2aa6d7aa3..9be97dac9e4207 100644 --- a/Mathlib/ModelTheory/Encoding.lean +++ b/Mathlib/ModelTheory/Encoding.lean @@ -93,8 +93,7 @@ theorem listDecode_encode_list (l : List (L.Term α)) : /-- An encoding of terms as lists. -/ @[simps] -protected def encoding : Encoding (L.Term α) where - Γ := α ⊕ (Σ i, L.Functions i) +protected def encoding : Encoding (L.Term α) (α ⊕ (Σ i, L.Functions i)) where encode := listEncode decode l := (listDecode l).head?.join decode_encode t := by @@ -275,8 +274,8 @@ theorem listDecode_encode_list (l : List (Σ n, L.BoundedFormula α n)) : /-- An encoding of bounded formulas as lists. -/ @[simps] -protected def encoding : Encoding (Σ n, L.BoundedFormula α n) where - Γ := (Σ k, L.Term (α ⊕ Fin k)) ⊕ ((Σ n, L.Relations n) ⊕ ℕ) +protected def encoding : Encoding (Σ n, L.BoundedFormula α n) + ((Σ k, L.Term (α ⊕ Fin k)) ⊕ ((Σ n, L.Relations n) ⊕ ℕ)) where encode φ := φ.2.listEncode decode l := (listDecode l)[0]? decode_encode φ := by @@ -292,8 +291,7 @@ theorem listEncode_sigma_injective : theorem card_le : #(Σ n, L.BoundedFormula α n) ≤ max ℵ₀ (Cardinal.lift.{max u v} #α + Cardinal.lift.{u'} L.card) := by refine lift_le.1 (BoundedFormula.encoding.card_le_card_list.trans ?_) - rw [encoding_Γ, mk_list_eq_max_mk_aleph0, lift_max, lift_aleph0, lift_max, lift_aleph0, - max_le_iff] + rw [mk_list_eq_max_mk_aleph0, lift_max, lift_aleph0, lift_max, lift_aleph0, max_le_iff] refine ⟨?_, le_max_left _ _⟩ rw [mk_sum, Term.card_sigma, mk_sum, ← add_eq_max le_rfl, mk_sum, mk_nat] simp only [lift_add, lift_lift, lift_aleph0] diff --git a/Mathlib/ModelTheory/PartialEquiv.lean b/Mathlib/ModelTheory/PartialEquiv.lean index 37151cfd3afc4b..db1240cd858093 100644 --- a/Mathlib/ModelTheory/PartialEquiv.lean +++ b/Mathlib/ModelTheory/PartialEquiv.lean @@ -10,6 +10,7 @@ public import Mathlib.Order.Ideal /-! # Partial Isomorphisms + This file defines partial isomorphisms between first-order structures. ## Main Definitions @@ -27,6 +28,8 @@ This file defines partial isomorphisms between first-order structures. element `m : M` in its domain. ## Main Results +- `FirstOrder.Language.exists_cardinalLTEquiv_of_embeddings` builds a `< κ`-generated partial + equivalence from two embeddings out of a common structure and a finite tuple. - `FirstOrder.Language.embedding_from_cg` shows that if structures `M` and `N` form an equivalence pair with `M` countably-generated, then any finite-generated partial equivalence between them can be extended to an embedding `M ↪[L] N`. @@ -390,6 +393,34 @@ abbrev FGEquiv := {f : M ≃ₚ[L] N // f.dom.FG} /-- The type of equivalences between substructures generated by fewer than `κ` elements. -/ abbrev CardinalLTEquiv (κ : Cardinal) := {f : M ≃ₚ[L] N // f.dom.CardinalLTGenerated κ} +variable {M N L} + +/-- Build a `< κ`-generated partial equivalence between `M` and `N` from a pair of embeddings +`f : A ↪[L] M` and `g : A ↪[L] N` together with a finite tuple `a : α → A`. The domain is the +substructure generated by the range of `f ∘ a`, and the equivalence agrees with `g ∘ a` on the +images of the `a i`. -/ +theorem exists_cardinalLTEquiv_of_embeddings + {α : Type*} [Finite α] {κ : Cardinal} (hκ : Cardinal.aleph0 ≤ κ) + {A : Type*} [L.Structure A] (f : A ↪[L] M) (g : A ↪[L] N) (a : α → A) : + ∃ (p₀ : L.CardinalLTEquiv M N κ) (hp_dom : ∀ i, f (a i) ∈ (p₀ : M ≃ₚ[L] N).dom), + ∀ i, ((p₀ : M ≃ₚ[L] N).toEquiv ⟨f (a i), hp_dom i⟩ : N) = g (a i) := by + let S : L.Substructure M := Substructure.closure L (Set.range (f ∘ a)) + have hSrange : S ≤ f.toHom.range := + Substructure.closure_le.mpr <| Set.range_subset_iff.mpr fun i => f.toHom.mem_range_self (a i) + let k : S ↪[L] N := g.comp (f.equivRange.symm.toEmbedding.comp (Substructure.inclusion hSrange)) + have hS_cardinal : S.CardinalLTGenerated κ := by + have hS_finite : (Set.range (f ∘ a)).Finite := Set.finite_range (f ∘ a) + haveI : Finite (Set.range (f ∘ a)) := hS_finite.to_subtype + exact Substructure.cardinalLTGenerated_closure + (hasCardinalLT_of_finite (Set.range (f ∘ a)) κ hκ) + refine ⟨⟨{ dom := S, cod := k.toHom.range, toEquiv := k.equivRange }, hS_cardinal⟩, + fun i => Substructure.subset_closure ⟨i, rfl⟩, fun i => ?_⟩ + change g (f.equivRange.symm (Substructure.inclusion hSrange _)) = g (a i) + congr 1 + exact f.equivRange.injective (Subtype.ext (by simp)) + +variable (M) (N) (L) + /-- Two structures `M` and `N` form an extension pair if the domain of any finitely-generated map from `M` to `N` can be extended to include any element of `M`. -/ def IsExtensionPair : Prop := ∀ (f : L.FGEquiv M N) (m : M), ∃ g, m ∈ g.1.dom ∧ f ≤ g @@ -545,8 +576,6 @@ theorem equiv_between_cg (M_cg : Structure.CG L M) (N_cg : Structure.CG L N) rw [toEquivOfEqTop_toEmbedding] apply Embedding.toPartialEquiv_toEmbedding -@[deprecated (since := "2026-06-13")] alias equiv_from_cg := equiv_between_cg - end FGEquiv end Language diff --git a/Mathlib/ModelTheory/QuantifierElimination.lean b/Mathlib/ModelTheory/QuantifierElimination.lean index bb68652067471a..7c3bcb7c7687c4 100644 --- a/Mathlib/ModelTheory/QuantifierElimination.lean +++ b/Mathlib/ModelTheory/QuantifierElimination.lean @@ -5,9 +5,9 @@ Authors: Yağız Kaan Aydoğdu, Yusuf Demir, Salih Erdem Koçak -/ module -public import Mathlib.Data.Finite.Sum public import Mathlib.ModelTheory.Complexity public import Mathlib.ModelTheory.Satisfiability +public import Mathlib.ModelTheory.ElementaryExtensionPair /-! # Quantifier Elimination @@ -36,10 +36,18 @@ criteria for establishing it. - `FirstOrder.Language.Theory.hasQuantifierElimination_of_exists_realize_of_embeddings` is a witness-transfer criterion for quantifier elimination. This corresponds to [Theorem 3.1.6][marker2002]. +- `FirstOrder.Language.Theory.hasQuantifierElimination_of_isElementaryExtensionPair` and + `FirstOrder.Language.Theory.hasQuantifierElimination_of_isElementaryExtensionPairFG` prove + quantifier elimination from the extension property appearing in + [Theorem 7.11][vandendries_henson_2016] and from a finitely generated variant. The theorem + `hasQuantifierElimination_of_isElementaryExtensionPairCardinalLTGenerated` + gives the corresponding `< κ`-generated version. ## References - [D. Marker, *Model Theory: An Introduction*][marker2002] +- [L. van den Dries and C. W. Henson, *Lecture Notes for Mathematics 571 Fall 2016 Model + Theory*][vandendries_henson_2016] -/ @[expose] public section @@ -489,6 +497,132 @@ theorem hasQuantifierElimination_of_exists_realize_of_embeddings {T : L.Theory} ⟨c, (BoundedFormula.realize_toFormula_snoc θ (g ∘ a) c).mpr hc⟩ exact ⟨b, (BoundedFormula.realize_toFormula_snoc θ (f ∘ a) b).mp hb⟩ +/-- A quantifier-free formula is realized at a tuple in the domain of a partial equivalence iff +it is realized at the image tuple in the codomain. -/ +private theorem isQF_realize_partialEquiv + {α : Type*} {M N : Type*} [L.Structure M] [L.Structure N] + {φ : L.Formula α} (hφ : φ.IsQF) (p : M ≃ₚ[L] N) + {v : α → M} (hv : ∀ i, v i ∈ p.dom) : + φ.Realize (fun i => (p.toEquiv ⟨v i, hv i⟩ : N)) ↔ φ.Realize v := by + let vdom : α → p.dom := fun i => ⟨v i, hv i⟩ + -- View the tuple inside the domain substructure: both the inclusion `p.dom ↪ M` and the partial + -- equivalence `p.dom ↪ N` are embeddings, so each preserves quantifier-free realization. + have hdom : φ.Realize v ↔ φ.Realize vdom := by + simpa [Formula.Realize, vdom, Function.comp_def, + Unique.eq_default (fun x : Fin 0 => (((default : Fin 0 → p.dom) x : p.dom) : M))] using + hφ.realize_embedding (f := (p.dom.subtype : p.dom ↪[L] M)) (v := vdom) (xs := default) + have hcod : φ.Realize (fun i => (p.toEquiv ⟨v i, hv i⟩ : N)) ↔ φ.Realize vdom := by + simpa [Formula.Realize, vdom, Function.comp_def, PartialEquiv.toEmbedding, + Unique.eq_default (fun x : Fin 0 => (p.toEquiv ((default : Fin 0 → p.dom) x) : N))] using + hφ.realize_embedding (f := p.toEmbedding) (v := vdom) (xs := default) + exact hcod.trans hdom.symm + +/-- Given a partial equivalence `p₀ : M ≃ₚ[L] N` together with an elementary embedding +`e : N ↪ₑ[L] N'` and a partial equivalence `q : M ≃ₚ[L] N'` extending the codomain-mapped +`PartialEquiv.codMap p₀ e`, transport a realization of a quantifier-free formula along `q`. -/ +private theorem exists_realize_codMap_of_extends + {α : Type} [Finite α] {φ : L.Formula (α ⊕ Fin 1)} (hφ : φ.IsQF) + {M N N' A : Type max u v} [L.Structure M] [L.Structure N] [L.Structure N'] [L.Structure A] + (p₀ : M ≃ₚ[L] N) (e : N ↪ₑ[L] N') + (q : M ≃ₚ[L] N') (hpq : PartialEquiv.codMap p₀ e.toEmbedding ≤ q) + (f : A ↪[L] M) (g : A ↪[L] N) (a : α → A) (b : M) + (hp_dom : ∀ i, f (a i) ∈ p₀.dom) + (hp_apply : ∀ i, (p₀.toEquiv ⟨f (a i), hp_dom i⟩ : N) = g (a i)) + (hbq : b ∈ q.dom) (hb : φ.Realize (Sum.elim (f ∘ a) (Fin.snoc default b))) : + ∃ b' : N', φ.Realize (Sum.elim ((e.toEmbedding ∘ g) ∘ a) (Fin.snoc default b')) := by + -- As `q` extends `codMap p₀ e`, the generators `f (a i)` lie in `q.dom` and `q` sends them to + -- `e (g (a i))`. Transporting the witness `b` along `q` then preserves the realization. + have hqa_dom (i : α) : f (a i) ∈ q.dom := PartialEquiv.dom_le_dom hpq (hp_dom i) + have hqa_apply (i : α) : (q.toEquiv ⟨f (a i), hqa_dom i⟩ : N') = e (g (a i)) := by + have hx' := congr_arg (fun y : q.cod => (y : N')) + (PartialEquiv.toEquiv_inclusion_apply hpq + (⟨f (a i), hp_dom i⟩ : (PartialEquiv.codMap p₀ e.toEmbedding).dom)) + change (q.toEquiv ⟨f (a i), hqa_dom i⟩ : N') = + e ((p₀.toEquiv ⟨f (a i), hp_dom i⟩ : N)) at hx' + rw [hx', hp_apply] + let vM : α ⊕ Fin 1 → M := Sum.elim (f ∘ a) (Fin.snoc default b) + have hvM (i : α ⊕ Fin 1) : vM i ∈ q.dom := by + cases i with + | inl x => simpa [vM, Function.comp_def] using hqa_dom x + | inr _ => simpa [vM, Fin.snoc] using hbq + refine ⟨q.toEquiv ⟨b, hbq⟩, ?_⟩ + have hqreal : φ.Realize (fun i : α ⊕ Fin 1 => (q.toEquiv ⟨vM i, hvM i⟩ : N')) := + (isQF_realize_partialEquiv hφ q hvM).mpr (by simpa [vM] using hb) + convert hqreal using 1 + funext i + cases i with + | inl x => simpa [vM, Function.comp_def] using (hqa_apply x).symm + | inr j => simp [vM, Fin.snoc, Subsingleton.elim j 0] + +/-- Pull a realization of a quantifier-free formula back through an elementary embedding to obtain +a witness in the source structure. -/ +private theorem exists_realize_descent_through_elementary + {α : Type} [Finite α] {φ : L.Formula (α ⊕ Fin 1)} + {N N' : Type max u v} [L.Structure N] [L.Structure N'] + (e : N ↪ₑ[L] N') (ga : α → N) (b' : N') + (htarget : φ.Realize (Sum.elim (e.toEmbedding ∘ ga) (Fin.snoc default b'))) : + ∃ c : N, φ.Realize (Sum.elim ga (Fin.snoc default c)) := by + -- The witness over `α ⊕ Fin 1` is the existential closure `θ.ex`, which the elementary embedding + -- `e` reflects from `N'` back to `N`. + let θ : L.BoundedFormula α 1 := BoundedFormula.relabel (id : α ⊕ Fin 1 → α ⊕ Fin 1) φ + have hθN' : θ.ex.Realize (e.toEmbedding ∘ ga) default := + BoundedFormula.realize_ex.mpr + ⟨b', (BoundedFormula.realize_relabel_id_snoc φ (e.toEmbedding ∘ ga) b').mpr htarget⟩ + have hθN : θ.ex.Realize ga default := + (e.map_boundedFormula θ.ex ga default).mp + (by simpa [Function.comp_def, Unique.eq_default] using hθN') + obtain ⟨c, hc⟩ := BoundedFormula.realize_ex.mp hθN + exact ⟨c, (BoundedFormula.realize_relabel_id_snoc φ ga c).mp hc⟩ + +/-- If, for every pair of nonempty models of `T`, the `< κ`-generated elementary extension-pair +property holds for an infinite `κ`, then `T` has quantifier elimination. + +The hypothesis is a `< κ`-generated variant of the extension property appearing as condition (2) +in [Theorem 7.11][vandendries_henson_2016]. -/ +theorem hasQuantifierElimination_of_isElementaryExtensionPairCardinalLTGenerated + {T : L.Theory} {κ : Cardinal} (hκ : Cardinal.aleph0 ≤ κ) + (h : ∀ ⦃M N : Type (max u v)⦄ [L.Structure M] [L.Structure N] + [T.Model M] [T.Model N] [Nonempty M] [Nonempty N], + L.IsElementaryExtensionPairCardinalLTGenerated κ M N) : + T.HasQuantifierElimination := by + refine hasQuantifierElimination_of_exists_realize_of_embeddings (T := T) ?_ + intro α _ φ hφ M N A _ _ _ _ _ _ _ f g a hM + obtain ⟨b, hb⟩ := hM + obtain ⟨p₀, hp_dom, hp_apply⟩ := exists_cardinalLTEquiv_of_embeddings hκ f g a + obtain ⟨N', hN', e, q₀, hbq, hpq⟩ := h p₀ b + letI : L.Structure N' := hN' + obtain ⟨b', hb'⟩ := exists_realize_codMap_of_extends hφ (p₀ : M ≃ₚ[L] N) e + (q₀ : M ≃ₚ[L] N') hpq f g a b hp_dom hp_apply hbq hb + exact exists_realize_descent_through_elementary e (g ∘ a) b' hb' + +/-- If every pair of nonempty models of `T` has the finitely generated elementary extension-pair +property, then `T` has quantifier elimination. + +The hypothesis is a finitely generated variant of the extension property appearing as condition +(2) in [Theorem 7.11][vandendries_henson_2016]. -/ +theorem hasQuantifierElimination_of_isElementaryExtensionPairFG + {T : L.Theory} + (h : ∀ ⦃M N : Type (max u v)⦄ [L.Structure M] [L.Structure N] + [T.Model M] [T.Model N] [Nonempty M] [Nonempty N], + L.IsElementaryExtensionPairFG M N) : + T.HasQuantifierElimination := + hasQuantifierElimination_of_isElementaryExtensionPairCardinalLTGenerated le_rfl + fun ⦃M N⦄ _ _ _ _ _ _ => (@h M N _ _ _ _ _ _).toCardinalLTGenerated_aleph0 + +/-- If every pair of nonempty models of `T` has the elementary extension-pair property, then `T` +has quantifier elimination. + +The hypothesis is condition (2) in [Theorem 7.11][vandendries_henson_2016]; this theorem proves +the implication from that extension property to condition (1). -/ +theorem hasQuantifierElimination_of_isElementaryExtensionPair + {T : L.Theory} + (h : ∀ ⦃M N : Type (max u v)⦄ [L.Structure M] [L.Structure N] + [T.Model M] [T.Model N] [Nonempty M] [Nonempty N], + L.IsElementaryExtensionPair M N) : + T.HasQuantifierElimination := + hasQuantifierElimination_of_isElementaryExtensionPairFG + fun ⦃M N⦄ _ _ _ _ _ _ => (@h M N _ _ _ _ _ _).FG + end Theory end Language diff --git a/Mathlib/ModelTheory/Satisfiability.lean b/Mathlib/ModelTheory/Satisfiability.lean index ff95887b9bc350..c36c9f809fde4c 100644 --- a/Mathlib/ModelTheory/Satisfiability.lean +++ b/Mathlib/ModelTheory/Satisfiability.lean @@ -196,7 +196,7 @@ variable (L) into `M`, but is not by type a substructure of `M`, and thus can be chosen to belong to the universe of the cardinal `κ`. -/ -theorem exists_elementaryEmbedding_card_eq_of_le (M : Type w') [L.Structure M] [Nonempty M] +theorem exists_elementaryEmbedding_card_eq_of_le (M : Type w') [L.Structure M] (κ : Cardinal.{w}) (h1 : ℵ₀ ≤ κ) (h2 : lift.{w} L.card ≤ Cardinal.lift.{max u v} κ) (h3 : lift.{w'} κ ≤ Cardinal.lift.{w} #M) : ∃ N : Bundled L.Structure, Nonempty (N ↪ₑ[L] M) ∧ #N = κ := by diff --git a/Mathlib/ModelTheory/Semantics.lean b/Mathlib/ModelTheory/Semantics.lean index 17d3768dcde50d..237353d641144a 100644 --- a/Mathlib/ModelTheory/Semantics.lean +++ b/Mathlib/ModelTheory/Semantics.lean @@ -391,7 +391,7 @@ theorem realize_relabel {m n : ℕ} {φ : L.BoundedFormula α n} {g : α → β apply realize_mapTermRel_add_castLe <;> simp theorem realize_liftAt {n n' m : ℕ} {φ : L.BoundedFormula α n} {v : α → M} {xs : Fin (n + n') → M} - (hmn : m + n' ≤ n + 1) : + (hmn : m ≤ n) : (φ.liftAt n' m).Realize v xs ↔ φ.Realize v (xs ∘ fun i => if ↑i < m then Fin.castAdd n' i else Fin.addNat i n') := by rw [liftAt] @@ -402,7 +402,7 @@ theorem realize_liftAt {n n' m : ℕ} {φ : L.BoundedFormula α n} {v : α → M | imp _ _ ih1 ih2 => simp only [mapTermRel, Realize, ih1 hmn, ih2 hmn] | @all k _ ih3 => have h : k + 1 + n' = k + n' + 1 := by rw [add_assoc, add_comm 1 n', ← add_assoc] - simp only [mapTermRel, Realize, realize_castLE_of_eq h, ih3 (hmn.trans k.succ.le_succ)] + simp only [mapTermRel, Realize, realize_castLE_of_eq h, ih3 (hmn.trans k.le_succ)] refine forall_congr' fun x => iff_eq_eq.mpr (congr rfl (funext (Fin.lastCases ?_ fun i => ?_))) · simp only [Function.comp_apply, val_last, snoc_last] refine (congr rfl (Fin.ext ?_)).trans (snoc_last _ _) @@ -898,6 +898,17 @@ theorem realize_toFormula_snoc (φ : L.BoundedFormula α 1) (v : α → M) (b : · exact Sum.elim_comp_inl v (Fin.snoc default b) · exact Sum.elim_comp_inr v (Fin.snoc default b) +/-- View a formula over `α ⊕ Fin 1` as a bounded formula over `α` with one bound variable. -/ +theorem realize_relabel_id_snoc (φ : L.Formula (α ⊕ Fin 1)) (v : α → M) (b : M) : + (BoundedFormula.relabel (id : α ⊕ Fin 1 → α ⊕ Fin 1) φ).Realize v + (Fin.snoc default b) ↔ + φ.Realize (Sum.elim v (Fin.snoc default b)) := by + rw [realize_relabel] + simp only [Function.comp_id, Fin.castAdd_zero, Fin.cast_refl] + rw [Subsingleton.elim (Fin.snoc (default : Fin 0 → M) b ∘ Fin.natAdd 1 : Fin 0 → M) + default] + rfl + @[simp] theorem realize_iSup [Finite β] {f : β → L.BoundedFormula α n} {v : α → M} {v' : Fin n → M} : diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean b/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean index 4c83802fcc713f..60325c75a83656 100644 --- a/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean +++ b/Mathlib/NumberTheory/ArithmeticFunction/Defs.lean @@ -294,7 +294,6 @@ instance [CommSemiring R] : CommSemiring (ArithmeticFunction R) where instance [CommRing R] : CommRing (ArithmeticFunction R) where neg_add_cancel := neg_add_cancel mul_comm := mul_comm - zsmul n f := n • f instance {S : Type*} [Semiring R] [AddCommMonoid S] [Module R S] : Module R (ArithmeticFunction S) where diff --git a/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean b/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean index 15a06a3780598d..bc9919c9511070 100644 --- a/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean +++ b/Mathlib/NumberTheory/ArithmeticFunction/LFunction.lean @@ -32,7 +32,7 @@ We take the following route from polynomials to L-functions: For example, the Riemann zeta function `ζ(s)` corresponds to taking `1 - T` at each prime `p`. For context, here is a diagram of the possible routes from polynomials to L-functions: - +``` T=q⁻ˢ s ∈ ℂ [polynomials in T] ----> [polynomials in q⁻ˢ] ----> [analytic function in s] | | | @@ -43,6 +43,7 @@ For context, here is a diagram of the possible routes from polynomials to L-func | (product) | (product) | (product) v T=q⁻ˢ V s ∈ ℂ V [multivariate power series] ----> [Dirichlet series] ----> [L-function in s] (the Euler product) +``` -/ @[expose] public section diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean b/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean index 0978dd9917eae5..11f9445eac20a4 100644 --- a/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean +++ b/Mathlib/NumberTheory/ArithmeticFunction/Misc.lean @@ -456,8 +456,8 @@ open Lean Meta Qq /-- Extension for `ArithmeticFunction.sigma`. -/ @[positivity ArithmeticFunction.sigma _ _] -meta def evalArithmeticFunctionSigma : PositivityExt where eval {u α} z p? e := do - let some p := p? | throwError "no PartialOrder instance" +meta def evalArithmeticFunctionSigma : PositivityExt where eval {u α} z p? e := + match p? with | none => throwError "no PartialOrder instance" | some p => do match u, α, e with | 0, ~q(ℕ), ~q(ArithmeticFunction.sigma $k $n) => assumeInstancesCommute diff --git a/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean b/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean index f0595aca62f502..3c7e3486d06ac2 100644 --- a/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean +++ b/Mathlib/NumberTheory/ArithmeticFunction/Zeta.lean @@ -222,8 +222,8 @@ open Lean Meta Qq /-- Extension for `ArithmeticFunction.zeta`. -/ @[positivity ArithmeticFunction.zeta _] -meta def evalArithmeticFunctionZeta : PositivityExt where eval {u α} z p? e := do - let some p := p? | throwError "no PartialOrder instance" +meta def evalArithmeticFunctionZeta : PositivityExt where eval {u α} z p? e := + match p? with | none => throwError "no PartialOrder instance" | some p => do match u, α, e with | 0, ~q(ℕ), ~q(ArithmeticFunction.zeta $n) => assumeInstancesCommute diff --git a/Mathlib/NumberTheory/Bernoulli.lean b/Mathlib/NumberTheory/Bernoulli.lean index 1e723409e73cf4..9a289e32988008 100644 --- a/Mathlib/NumberTheory/Bernoulli.lean +++ b/Mathlib/NumberTheory/Bernoulli.lean @@ -262,7 +262,8 @@ theorem bernoulli_spec' (n : ℕ) : convert! eq_sub_of_add_eq' H using 1 · refine sum_congr rfl fun p h => ?_ obtain ⟨h', h''⟩ : p ∈ _ ∧ p ≠ _ := by rwa [mem_sdiff, mem_singleton] at h - simp [bernoulli_eq_bernoulli'_of_ne_one ((not_congr (antidiagonal_congr h' h₁)).mp h'')] + simp [bernoulli_eq_bernoulli'_of_ne_one + ((not_congr (HasAntidiagonal.antidiagonal_congr h' h₁)).mp h'')] · simp [field, h₃] norm_num diff --git a/Mathlib/NumberTheory/ClassNumber/AdmissibleAbs.lean b/Mathlib/NumberTheory/ClassNumber/AdmissibleAbs.lean index d8552bc6d3a10c..9462819c2bd446 100644 --- a/Mathlib/NumberTheory/ClassNumber/AdmissibleAbs.lean +++ b/Mathlib/NumberTheory/ClassNumber/AdmissibleAbs.lean @@ -11,6 +11,7 @@ public import Mathlib.NumberTheory.ClassNumber.AdmissibleAbsoluteValue /-! # Admissible absolute value on the integers + This file defines an admissible absolute value `AbsoluteValue.absIsAdmissible` which we use to show the class number of the ring of integers of a number field is finite. diff --git a/Mathlib/NumberTheory/ClassNumber/AdmissibleAbsoluteValue.lean b/Mathlib/NumberTheory/ClassNumber/AdmissibleAbsoluteValue.lean index 681bb81828ef4f..9bb6058a90ae1f 100644 --- a/Mathlib/NumberTheory/ClassNumber/AdmissibleAbsoluteValue.lean +++ b/Mathlib/NumberTheory/ClassNumber/AdmissibleAbsoluteValue.lean @@ -11,6 +11,7 @@ public import Mathlib.Algebra.Order.AbsoluteValue.Euclidean /-! # Admissible absolute values + This file defines a structure `AbsoluteValue.IsAdmissible` which we use to show the class number of the ring of integers of a global field is finite. diff --git a/Mathlib/NumberTheory/ClassNumber/Finite.lean b/Mathlib/NumberTheory/ClassNumber/Finite.lean index 09e640cfb8fd15..2c77f086215cd8 100644 --- a/Mathlib/NumberTheory/ClassNumber/Finite.lean +++ b/Mathlib/NumberTheory/ClassNumber/Finite.lean @@ -15,6 +15,7 @@ public import Mathlib.RingTheory.Norm.Basic /-! # Class numbers of global fields + In this file, we use the notion of "admissible absolute value" to prove finiteness of the class group for number fields and function fields. diff --git a/Mathlib/NumberTheory/Cyclotomic/CyclotomicCharacter.lean b/Mathlib/NumberTheory/Cyclotomic/CyclotomicCharacter.lean index cd69509024e11b..eaa68d6f06b3d3 100644 --- a/Mathlib/NumberTheory/Cyclotomic/CyclotomicCharacter.lean +++ b/Mathlib/NumberTheory/Cyclotomic/CyclotomicCharacter.lean @@ -121,7 +121,7 @@ theorem modularCyclotomicCharacter.pow_dvd_aux_pow_sub_aux_pow `modularCyclotomicCharacter.toFun n g` is the `j : ZMod d` such that `g(ζ)=ζ^j` for all `n`-th roots of unity. Here `d` is the number of `n`th roots of unity in `L`. -/ noncomputable def modularCyclotomicCharacter.toFun (n : ℕ) [NeZero n] (g : L ≃+* L) : - ZMod (Fintype.card (rootsOfUnity n L)) := + ZMod (Nat.card (rootsOfUnity n L)) := modularCyclotomicCharacter.aux g n namespace modularCyclotomicCharacter @@ -134,7 +134,7 @@ theorem toFun_spec (g : L ≃+* L) {n : ℕ} [NeZero n] (t : rootsOfUnity n L) : rw [modularCyclotomicCharacter.aux_spec g n t, ← zpow_natCast, modularCyclotomicCharacter.toFun, ZMod.val_intCast, ← Subgroup.coe_zpow] exact Units.ext_iff.1 <| SetCoe.ext_iff.2 <| - zpow_eq_zpow_emod _ pow_card_eq_one (G := rootsOfUnity n L) + zpow_eq_zpow_emod _ pow_card_eq_one' (G := rootsOfUnity n L) theorem toFun_spec' (g : L ≃+* L) {n : ℕ} [NeZero n] {t : Lˣ} (ht : t ∈ rootsOfUnity n L) : g t = t ^ (χ₀ n g).val := @@ -145,24 +145,24 @@ theorem toFun_spec'' (g : L ≃+* L) {n : ℕ} [NeZero n] {t : L} (ht : IsPrimit toFun_spec' g (SetLike.coe_mem ht.toRootsOfUnity) /-- If g(t)=t^c for all roots of unity, then c=χ(g). -/ -theorem toFun_unique (g : L ≃+* L) (c : ZMod (Fintype.card (rootsOfUnity n L))) +theorem toFun_unique (g : L ≃+* L) (c : ZMod (Nat.card (rootsOfUnity n L))) (hc : ∀ t : rootsOfUnity n L, g (t : Lˣ) = (t ^ c.val : Lˣ)) : c = χ₀ n g := by - apply IsCyclic.ext Nat.card_eq_fintype_card (fun ζ ↦ ?_) + apply IsCyclic.ext rfl (fun ζ ↦ ?_) specialize hc ζ suffices ((ζ ^ c.val : Lˣ) : L) = (ζ ^ (χ₀ n g).val : Lˣ) by exact_mod_cast this rw [← toFun_spec g ζ, hc] -theorem toFun_unique' (g : L ≃+* L) (c : ZMod (Fintype.card (rootsOfUnity n L))) +theorem toFun_unique' (g : L ≃+* L) (c : ZMod (Nat.card (rootsOfUnity n L))) (hc : ∀ t ∈ rootsOfUnity n L, g t = t ^ c.val) : c = χ₀ n g := toFun_unique n g c (fun ⟨_, ht⟩ ↦ hc _ ht) lemma id : χ₀ n (RingEquiv.refl L) = 1 := by refine (toFun_unique n (RingEquiv.refl L) 1 <| fun t ↦ ?_).symm - have : 1 ≤ Fintype.card { x // x ∈ rootsOfUnity n L } := Fin.size_positive' + have : 1 ≤ Nat.card { x // x ∈ rootsOfUnity n L } := Nat.card_pos obtain (h | h) := this.lt_or_eq · have := Fact.mk h simp [ZMod.val_one] - · have := Fintype.card_le_one_iff_subsingleton.mp h.ge + · have := Finite.card_le_one_iff_subsingleton.mp h.ge obtain rfl : t = 1 := Subsingleton.elim t 1 simp @@ -175,7 +175,7 @@ lemma comp (g h : L ≃+* L) : χ₀ n (g * h) = congr 2 norm_cast simp only [pow_eq_pow_iff_modEq, ← ZMod.natCast_eq_natCast_iff, - ZMod.natCast_val, Nat.cast_mul, ZMod.cast_mul (m := orderOf ζ) orderOf_dvd_card] + ZMod.natCast_val, Nat.cast_mul, ZMod.cast_mul (m := orderOf ζ) (orderOf_dvd_natCard _)] end modularCyclotomicCharacter @@ -188,18 +188,18 @@ characterised by the property that `g(ζ)=ζ^(modularCyclotomicCharacter n g)` for `g` an automorphism of `L` and `ζ` an `n`th root of unity. -/ noncomputable def modularCyclotomicCharacter' (n : ℕ) [NeZero n] : - (L ≃+* L) →* (ZMod (Fintype.card { x // x ∈ rootsOfUnity n L }))ˣ := MonoidHom.toHomUnits + (L ≃+* L) →* (ZMod (Nat.card { x // x ∈ rootsOfUnity n L }))ˣ := MonoidHom.toHomUnits { toFun := modularCyclotomicCharacter.toFun n map_one' := modularCyclotomicCharacter.id n map_mul' := modularCyclotomicCharacter.comp n } lemma modularCyclotomicCharacter'.spec' (g : L ≃+* L) {t : Lˣ} (ht : t ∈ rootsOfUnity n L) : g t = t ^ ((modularCyclotomicCharacter' L n g) : ZMod - (Fintype.card { x // x ∈ rootsOfUnity n L })).val := + (Nat.card { x // x ∈ rootsOfUnity n L })).val := modularCyclotomicCharacter.toFun_spec' g ht lemma modularCyclotomicCharacter'.unique' (g : L ≃+* L) - {c : ZMod (Fintype.card { x // x ∈ rootsOfUnity n L })} + {c : ZMod (Nat.card { x // x ∈ rootsOfUnity n L })} (hc : ∀ t ∈ rootsOfUnity n L, g t = t ^ c.val) : c = modularCyclotomicCharacter' L n g := modularCyclotomicCharacter.toFun_unique' _ _ _ hc @@ -210,14 +210,14 @@ automorphisms of `L` to `(ℤ/nℤ)ˣ`. It is uniquely characterised by the prop `g(ζ)=ζ^(modularCyclotomicCharacter n g)` for `g` an automorphism of `L` and `ζ` any `n`th root of unity. -/ noncomputable def modularCyclotomicCharacter {n : ℕ} [NeZero n] - (hn : Fintype.card { x // x ∈ rootsOfUnity n L } = n) : + (hn : Nat.card { x // x ∈ rootsOfUnity n L } = n) : (L ≃+* L) →* (ZMod n)ˣ := (Units.mapEquiv <| (ZMod.ringEquivCongr hn).toMulEquiv).toMonoidHom.comp (modularCyclotomicCharacter' L n) namespace modularCyclotomicCharacter -variable {n : ℕ} [NeZero n] (hn : Fintype.card { x // x ∈ rootsOfUnity n L } = n) +variable {n : ℕ} [NeZero n] (hn : Nat.card { x // x ∈ rootsOfUnity n L } = n) lemma spec (g : L ≃+* L) {t : Lˣ} (ht : t ∈ rootsOfUnity n L) : g t = t ^ ((modularCyclotomicCharacter L hn g) : ZMod n).val := by @@ -283,8 +283,8 @@ theorem toFun_apply : open modularCyclotomicCharacter in theorem toZModPow_toFun (n : ℕ) : (χ p g).toZModPow n = - (modularCyclotomicCharacter _ (Fintype.card_eq_nat_card.trans - (HasEnoughRootsOfUnity.natCard_rootsOfUnity L (p ^ n))) g).val := by + (modularCyclotomicCharacter _ + (HasEnoughRootsOfUnity.natCard_rootsOfUnity L (p ^ n)) g).val := by rw [toFun_apply] refine (PadicInt.toZModPow_ofIntSeq_of_pow_dvd_sub (aux g <| p ^ ·) _ (fun i ↦ pow_dvd_aux_pow_sub_aux_pow g p i.le_succ) n).trans ?_ @@ -328,8 +328,8 @@ theorem cyclotomicCharacter.spec (p : ℕ) [Fact p.Prime] {n : ℕ} theorem cyclotomicCharacter.toZModPow (p : ℕ) [Fact p.Prime] {n : ℕ} [∀ i, HasEnoughRootsOfUnity L (p ^ i)] (g : L ≃+* L) : (cyclotomicCharacter L p g).val.toZModPow n = - (modularCyclotomicCharacter _ (Fintype.card_eq_nat_card.trans - (HasEnoughRootsOfUnity.natCard_rootsOfUnity L (p ^ n))) g).val := + (modularCyclotomicCharacter _ + (HasEnoughRootsOfUnity.natCard_rootsOfUnity L (p ^ n)) g).val := toZModPow_toFun _ _ _ open IntermediateField in diff --git a/Mathlib/NumberTheory/Cyclotomic/Discriminant.lean b/Mathlib/NumberTheory/Cyclotomic/Discriminant.lean index afbbbb7152d1ea..21e681b2319ffd 100644 --- a/Mathlib/NumberTheory/Cyclotomic/Discriminant.lean +++ b/Mathlib/NumberTheory/Cyclotomic/Discriminant.lean @@ -11,6 +11,7 @@ public import Mathlib.NumberTheory.NumberField.Discriminant.Defs /-! # Discriminant of cyclotomic fields + We compute the discriminant of a `p ^ n`-th cyclotomic extension. ## Main results diff --git a/Mathlib/NumberTheory/Cyclotomic/Gal.lean b/Mathlib/NumberTheory/Cyclotomic/Gal.lean index a1953f31cffa15..fb8e0532ac4489 100644 --- a/Mathlib/NumberTheory/Cyclotomic/Gal.lean +++ b/Mathlib/NumberTheory/Cyclotomic/Gal.lean @@ -58,9 +58,9 @@ theorem autToPow_injective : Function.Injective <| hμ.autToPow K := by intro f g hfg have : f.toAlgHom = g.toAlgHom := by apply (hμ.powerBasis K).algHom_ext - rw [AlgEquiv.coe_algHom, AlgEquiv.coe_algHom, powerBasis_gen, + rw [AlgEquiv.coe_toAlgHom, AlgEquiv.coe_toAlgHom, powerBasis_gen, ← autToPow_spec K hμ g, ← autToPow_spec K hμ f, hfg] - exact AlgEquiv.coe_algHom_injective this + exact AlgEquiv.coe_toAlgHom_injective this end IsPrimitiveRoot @@ -89,9 +89,9 @@ noncomputable def autEquivPow (h : Irreducible (cyclotomic n K)) : Gal(L/K) ≃* exact ((zeta_spec n K L).minpoly_eq_cyclotomic_of_irreducible h).symm.trans hr) left_inv := fun f => by simp only [MonoidHom.toFun_eq_coe] - apply AlgEquiv.coe_algHom_injective + apply AlgEquiv.coe_toAlgHom_injective apply (hζ.powerBasis K).algHom_ext - simp only [AlgEquiv.coe_algHom] + simp only [AlgEquiv.coe_toAlgHom] rw [PowerBasis.equivOfMinpoly_gen] simp only [IsPrimitiveRoot.powerBasis_gen, IsPrimitiveRoot.autToPow_spec] right_inv := fun x => by diff --git a/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean b/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean index fa7216ad7b0435..39cdd13911878b 100644 --- a/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean +++ b/Mathlib/NumberTheory/Cyclotomic/PrimitiveRoots.lean @@ -17,6 +17,7 @@ public import Mathlib.RingTheory.SimpleModule.Basic /-! # Primitive roots in cyclotomic fields + If `IsCyclotomicExtension {n} A B`, we define an element `zeta n A B : B` that is a primitive `n`th-root of unity in `B` and we study its properties. We also prove related theorems under the more general assumption of just being a primitive root, for reasons described in the implementation diff --git a/Mathlib/NumberTheory/EllipticDivisibilitySequence.lean b/Mathlib/NumberTheory/EllipticDivisibilitySequence.lean index 3da2f1ca10dc78..649c86acda9939 100644 --- a/Mathlib/NumberTheory/EllipticDivisibilitySequence.lean +++ b/Mathlib/NumberTheory/EllipticDivisibilitySequence.lean @@ -5,6 +5,7 @@ Authors: David Kurniadi Angdinata -/ module +public import Mathlib.Data.Nat.DvdSequence public import Mathlib.Data.Nat.EvenOddRec public import Mathlib.Tactic.Linarith public import Mathlib.Tactic.LinearCombination @@ -30,7 +31,6 @@ Some examples of EDSs include ## Main definitions * `IsEllSequence`: a sequence indexed by integers is an elliptic sequence. -* `IsDivSequence`: a sequence indexed by integers is a divisibility sequence. * `IsEllDivSequence`: a sequence indexed by integers is an EDS. * `preNormEDS'`: the auxiliary sequence for a normalised EDS indexed by `ℕ`. * `preNormEDS`: the auxiliary sequence for a normalised EDS indexed by `ℤ`. @@ -83,23 +83,20 @@ def IsEllSequence : Prop := ∀ m n r : ℤ, W (m + n) * W (m - n) * W r ^ 2 = W (m + r) * W (m - r) * W n ^ 2 - W (n + r) * W (n - r) * W m ^ 2 -/-- The proposition that a sequence indexed by integers is a divisibility sequence. -/ -def IsDivSequence : Prop := - ∀ m n : ℕ, m ∣ n → W m ∣ W n +@[deprecated (since := "2026-06-30")] alias IsDivSequence := IsDvdSequence /-- The proposition that a sequence indexed by integers is an EDS. -/ def IsEllDivSequence : Prop := - IsEllSequence W ∧ IsDivSequence W + IsEllSequence W ∧ IsDvdSequence W lemma isEllSequence_id : IsEllSequence id := fun _ _ _ => by simp_rw [id_eq]; ring1 -lemma isDivSequence_id : IsDivSequence id := - fun _ _ => Int.ofNat_dvd.mpr +@[deprecated (since := "2026-06-30")] alias isDivSequence_id := IsDvdSequence.id /-- The identity sequence is an EDS. -/ theorem isEllDivSequence_id : IsEllDivSequence id := - ⟨isEllSequence_id, isDivSequence_id⟩ + ⟨isEllSequence_id, .id ℤ⟩ variable {W} @@ -107,8 +104,7 @@ lemma IsEllSequence.smul (h : IsEllSequence W) (x : R) : IsEllSequence (x • W) fun m n r => by linear_combination (norm := (simp_rw [Pi.smul_apply, smul_eq_mul]; ring1)) x ^ 4 * h m n r -lemma IsDivSequence.smul (h : IsDivSequence W) (x : R) : IsDivSequence (x • W) := - fun m n r => mul_dvd_mul_left x <| h m n r +@[deprecated (since := "2026-06-30")] alias IsDivSequence.smul := IsDvdSequence.smul lemma IsEllDivSequence.smul (h : IsEllDivSequence W) (x : R) : IsEllDivSequence (x • W) := ⟨h.left.smul x, h.right.smul x⟩ diff --git a/Mathlib/NumberTheory/FLT/Four.lean b/Mathlib/NumberTheory/FLT/Four.lean index ecaa9ad5254fa0..6f17dfbc8a98a0 100644 --- a/Mathlib/NumberTheory/FLT/Four.lean +++ b/Mathlib/NumberTheory/FLT/Four.lean @@ -13,6 +13,7 @@ public import Mathlib.Tactic.LinearCombination /-! # Fermat's Last Theorem for the case n = 4 + There are no non-zero integers `a`, `b` and `c` such that `a ^ 4 + b ^ 4 = c ^ 4`. -/ diff --git a/Mathlib/NumberTheory/FLT/Three.lean b/Mathlib/NumberTheory/FLT/Three.lean index 711bf57d10cf26..80e14f9531a959 100644 --- a/Mathlib/NumberTheory/FLT/Three.lean +++ b/Mathlib/NumberTheory/FLT/Three.lean @@ -13,6 +13,7 @@ public import Mathlib.Algebra.Ring.Divisibility.Lemmas /-! # Fermat Last Theorem in the case `n = 3` + The goal of this file is to prove Fermat's Last Theorem in the case `n = 3`. ## Main results diff --git a/Mathlib/NumberTheory/FactorisationProperties.lean b/Mathlib/NumberTheory/FactorisationProperties.lean index 00ed37e5c471c2..cea8ccdfdc3df0 100644 --- a/Mathlib/NumberTheory/FactorisationProperties.lean +++ b/Mathlib/NumberTheory/FactorisationProperties.lean @@ -81,6 +81,9 @@ theorem not_pseudoperfect_iff_forall : ¬ Pseudoperfect n ↔ n = 0 ∨ ∀ s ⊆ properDivisors n, ∑ i ∈ s, i ≠ n := by grind [Pseudoperfect] +theorem not_deficient_zero : ¬ Deficient 0 := by + decide + theorem deficient_one : Deficient 1 := by decide @@ -96,9 +99,21 @@ theorem not_abundant_zero : ¬ Abundant 0 := by theorem abundant_twelve : Abundant 12 := by decide +theorem not_weird_zero : ¬ Weird 0 := by + decide + theorem weird_seventy : Weird 70 := by decide +kernel +lemma Deficient.pos (h : Deficient n) : 0 < n := by + grind only [not_deficient_zero] + +lemma Abundant.pos (h : Abundant n) : 0 < n := by + grind only [not_abundant_zero] + +lemma Weird.pos (h : Weird n) : 0 < n := by + grind only [not_weird_zero] + lemma deficient_iff_not_abundant_and_not_perfect (hn : n ≠ 0) : Deficient n ↔ ¬ Abundant n ∧ ¬ Perfect n := by grind [Perfect, Abundant, Deficient] diff --git a/Mathlib/NumberTheory/Fermat.lean b/Mathlib/NumberTheory/Fermat.lean index c5b50d9a5d15ab..10b0bb353af85e 100644 --- a/Mathlib/NumberTheory/Fermat.lean +++ b/Mathlib/NumberTheory/Fermat.lean @@ -127,11 +127,11 @@ theorem pow_of_pow_add_prime {a n : ℕ} (ha : 1 < a) (hn : n ≠ 0) (hP : (a ^ rw [one_pow, hP.dvd_iff_eq (Nat.lt_add_right 1 ha).ne', add_left_inj, pow_eq_self_iff ha] at h rw [h, mul_one] -set_option backward.isDefEq.respectTransparency false in /-- `Fₙ = 2^(2^n)+1` is prime if `3^(2^(2^n-1)) = -1 mod Fₙ` (**Pépin's test**). -/ lemma pepin_primality (n : ℕ) (h : 3 ^ (2 ^ (2 ^ n - 1)) = (-1 : ZMod (fermatNumber n))) : (fermatNumber n).Prime := by have := Fact.mk (two_lt_fermatNumber n) + unfold fermatNumber at h this have key : 2 ^ n = 2 ^ n - 1 + 1 := (Nat.sub_add_cancel Nat.one_le_two_pow).symm apply lucas_primality (p := 2 ^ (2 ^ n) + 1) (a := 3) · rw [Nat.add_sub_cancel, key, pow_succ, pow_mul, ← pow_succ, ← key, h, neg_one_sq] diff --git a/Mathlib/NumberTheory/GaussSum.lean b/Mathlib/NumberTheory/GaussSum.lean index dec2c041cf88a3..9092406fa0325b 100644 --- a/Mathlib/NumberTheory/GaussSum.lean +++ b/Mathlib/NumberTheory/GaussSum.lean @@ -95,6 +95,49 @@ lemma star_gaussSum_eq (χ : MulChar R ℂ) (ψ : AddChar R ℂ) : end GaussSumDef +/-! +### Gauss sums of trivial characters +-/ + +section GaussSumTrivial + +variable {R R' : Type*} [CommRing R] [Fintype R] [CommRing R'] + +/-- The Gauss sum of the two trivial characters is the cardinality of the unit group of `R`. -/ +@[simp] +theorem gaussSum_one_one : gaussSum (1 : MulChar R R') (1 : AddChar R R') = Nat.card Rˣ := by + classical + simp [gaussSum, MulChar.sum_one_eq_card_units] + +/-- The Gauss sum of a nontrivial multiplicative character and the trivial additive character +vanishes. -/ +theorem gaussSum_one_right [IsDomain R'] {χ : MulChar R R'} (hχ : χ ≠ 1) : + gaussSum χ (1 : AddChar R R') = 0 := by + simpa [gaussSum] using MulChar.sum_eq_zero_of_ne_one hχ + +end GaussSumTrivial + +section GaussSumTrivialField + +variable {R R' : Type*} [Field R] [Fintype R] [CommRing R'] [IsDomain R'] + +/-- The Gauss sum of the trivial multiplicative character and a nontrivial additive character, +over a finite field, is `-1`. -/ +theorem gaussSum_one_left {ψ : AddChar R R'} (hψ : ψ ≠ 1) : + gaussSum (1 : MulChar R R') ψ = -1 := by + classical + simp only [gaussSum, ← add_eq_zero_iff_eq_neg] + calc ∑ a, (1 : MulChar R R') a * ψ a + 1 + _ = ∑ a ∈ {0}ᶜ, (1 : MulChar R R') a * ψ a + 1 := by + simp [← ({0} : Finset R).sum_compl_add_sum] + _ = ∑ a ∈ {0}ᶜ, ψ a + ψ 0 := by + congr! <;> aesop (add simp MulChar.one_apply) + _ = 0 := by + rw [← AddChar.sum_eq_zero_of_ne_one hψ, ← Finset.sum_compl_add_sum (s := {0})] + simp + +end GaussSumTrivialField + /-! ### The product of two Gauss sums -/ diff --git a/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean b/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean index 5d61f148abc523..3220e94f093a8a 100644 --- a/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean +++ b/Mathlib/NumberTheory/Harmonic/ZetaAsymp.lean @@ -1,12 +1,13 @@ /- Copyright (c) 2024 David Loeffler. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. -Authors: David Loeffler +Authors: David Loeffler, Terence Tao -/ module -public import Mathlib.NumberTheory.LSeries.RiemannZeta +public import Mathlib.NumberTheory.LSeries.Dirichlet public import Mathlib.NumberTheory.Harmonic.GammaDeriv +public import Mathlib.Analysis.Asymptotics.Lemmas /-! # Asymptotics of `ζ s` as `s → 1` or `s → 0` @@ -20,6 +21,10 @@ The goal of this file is to evaluate the limit of `ζ s - 1 / (s - 1)` as `s → * `deriv_riemannZeta_zero`: `ζ'(0) = -log(2π) / 2`, which derives from the above. * `riemannZeta_one_ne_zero`: with our definition of `ζ 1` (which is characterised as the limit of `ζ s - 1 / (s - 1) / Gammaℝ s` as `s → 1`), we have `ζ 1 ≠ 0`. +* Representation of `riemannZeta s` as `(s-1)⁻¹ + riemannZeta₀ s` or `(s-1)⁻¹ * riemannZeta₁ s` + for certain entire functions `riemannZeta₀` and `riemannZeta₁`. +* Asymptotics for `deriv riemannZeta s`, `log (riemannZeta s)`, + `(deriv riemannZeta s) / (riemannZeta s)` and `(riemannZeta s)⁻¹` as `s → 1`. ### Outline of arguments @@ -473,3 +478,162 @@ theorem deriv_riemannZeta_zero : open ComplexOrder in repeat rw [log_mul (by positivity) (by positivity) (by simp [arg, LT.lt.le, Real.pi_pos])] ring + +section near_one + +/-! +## More asymptotics near `s = 1` + +To facilitate the analysis of `riemannZeta` near `s = 1`, we write `riemannZeta s` additively as +`(s-1)⁻¹ + riemannZeta₀ s` and multiplicatively as `(s-1)⁻¹ * riemannZeta₁ s` for certain +entire functions `riemannZeta₀`, `riemannZeta₁`. +-/ + +open Asymptotics + +/-- `riemannZeta₀ s` is the completion of `riemannZeta s - (s-1)⁻¹` at `s = 1`. -/ +noncomputable def riemannZeta₀ (s : ℂ) : ℂ := + if s = 1 then γ else riemannZeta s - (s-1)⁻¹ + +/-- `riemannZeta₁ s` is the completion of `(s-1) * riemannZeta s` at `s = 1`. -/ +noncomputable def riemannZeta₁ (s : ℂ) : ℂ := 1 + (s - 1) * riemannZeta₀ s + +@[simp] +lemma riemannZeta₀_one : riemannZeta₀ 1 = γ := by simp [riemannZeta₀] + +@[simp] +lemma riemannZeta₁_one : riemannZeta₁ 1 = 1 := by simp [riemannZeta₁] + +lemma riemannZeta_eq_inv_sub_add {s : ℂ} (hs : s ≠ 1) : + riemannZeta s = (s - 1)⁻¹ + riemannZeta₀ s := by simp [riemannZeta₀, hs] + +lemma riemannZeta_eq_inv_sub_mul {s : ℂ} (hs : s ≠ 1) : + riemannZeta s = (s - 1)⁻¹ * riemannZeta₁ s := by grind [riemannZeta₁, riemannZeta₀] + +@[fun_prop] +lemma differentiable_riemannZeta₀ : Differentiable ℂ riemannZeta₀ := by + rw [← differentiableOn_univ, ← differentiableOn_compl_singleton_and_continuousAt_iff + (univ_mem : _ ∈ 𝓝 (1 : ℂ)), continuousAt_iff_punctured_nhds, ← compl_eq_univ_sdiff] + constructor + · refine .congr (f := fun s ↦ riemannZeta s - (s - 1)⁻¹) ?_ (by simp +contextual [riemannZeta₀]) + exact differentiableOn_riemannZeta.fun_sub (by fun_prop (disch := grind)) + · convert tendsto_nhdsWithin_congr ?_ tendsto_riemannZeta_sub_one_div <;> + simp +contextual [riemannZeta₀] + +@[fun_prop] +lemma differentiable_riemannZeta₁ : Differentiable ℂ riemannZeta₁ := by + unfold riemannZeta₁; fun_prop + +lemma riemannZeta₁_ne_zero_of_near_one : ∀ᶠ s in 𝓝 1, riemannZeta₁ s ≠ 0 := by + refine Tendsto.eventually_ne ?_ one_ne_zero + simpa using (differentiable_riemannZeta₁.continuous.continuousAt (x := 1)).tendsto + +@[simp] +lemma deriv_riemannZeta₁_one : deriv riemannZeta₁ 1 = γ := by + unfold riemannZeta₁ + rw [deriv_const_add, deriv_fun_mul (by fun_prop) (by fun_prop)] + simp + +lemma deriv_riemannZeta_eq_neg_inv_sub_sq_add {s : ℂ} (hs : s ≠ 1) : + deriv riemannZeta s = - ((s - 1)⁻¹) ^ 2 + deriv riemannZeta₀ s := by + have := sub_ne_zero_of_ne hs + convert EventuallyEq.deriv_eq (f := fun s ↦ (s - 1)⁻¹ + riemannZeta₀ s) ?_ + · rw [deriv_fun_add (by fun_prop) (by fun_prop), deriv_fun_inv'' (by fun_prop) (by exact this)] + simp [field] + · filter_upwards [compl_singleton_mem_nhds hs] using by grind [riemannZeta_eq_inv_sub_add] + +lemma deriv_riemannZeta_eq_neg_inv_sub_sq_mul_add {s : ℂ} (hs : s ≠ 1) : + deriv riemannZeta s = + - ((s - 1)⁻¹) ^ 2 * (riemannZeta₁ s) + (s - 1)⁻¹ * deriv riemannZeta₁ s := by + have := sub_ne_zero_of_ne hs + convert EventuallyEq.deriv_eq (f := fun s ↦ (s - 1)⁻¹ * riemannZeta₁ s) ?_ + · rw [deriv_fun_mul (by fun_prop) (by fun_prop), deriv_fun_inv'' (by fun_prop) (by exact this)] + simp [field] + · filter_upwards [compl_singleton_mem_nhds hs] using by grind [riemannZeta_eq_inv_sub_mul] + +lemma deriv_riemannZeta_add_inv_sub_sq_bounded : + (fun s ↦ deriv riemannZeta s + ((s - 1)⁻¹) ^ 2) =O[𝓝[≠] 1] (fun _ ↦ (1 : ℂ)) := + (differentiable_riemannZeta₀.deriv.continuous.continuousAt.isBigO.mono nhdsWithin_le_nhds).congr' + (eventually_nhdsWithin_of_forall (by simp +contextual [deriv_riemannZeta_eq_neg_inv_sub_sq_add])) + .rfl + +lemma log_riemannZeta_eq_neg_log_sub_add_ofReal {s : ℝ} (hs : s > 1) : + (riemannZeta s).re.log = - (s - 1).log + (riemannZeta₁ s).re.log := by + have : (riemannZeta s).re = (s - 1)⁻¹ * (riemannZeta₁ s).re := by + rw_mod_cast [riemannZeta_eq_inv_sub_mul (by aesop), re_ofReal_mul] + rw [this, Real.log_mul, Real.log_inv] <;> + grind [riemannZeta_re_pos_of_one_lt hs] + +lemma log_riemannZeta_add_log_sub_isBigO_ofReal : + (fun (s : ℝ) ↦ (riemannZeta s).re.log + (s - 1).log) =O[𝓝[>] 1] (· - 1) := by + suffices (fun (s : ℝ) ↦ (riemannZeta₁ s).re.log) =O[𝓝 1] (· - 1) by + refine (this.mono nhdsWithin_le_nhds).congr' + (eventually_nhdsWithin_of_forall (fun s hs ↦ ?_)) .rfl + simp [log_riemannZeta_eq_neg_log_sub_add_ofReal hs] + suffices DifferentiableAt ℝ (fun (s : ℝ) ↦ (riemannZeta₁ s).re.log) 1 by + simpa using this.isBigO_sub + have : Differentiable ℝ riemannZeta₀ := by fun_prop + fun_prop (disch := simp) + +lemma log_riemannZeta_add_log_sub_isLittleO_ofReal : + (fun (s : ℝ) ↦ (riemannZeta s).re.log + (s - 1).log) =o[𝓝[>] (1 : ℝ)] (fun _ ↦ (1 : ℝ)) := + log_riemannZeta_add_log_sub_isBigO_ofReal.trans_isLittleO + (continuous_id.continuousAt.isLittleO.mono nhdsWithin_le_nhds) + +lemma log_deriv_riemannZeta_eq_neg_inv_sub_add : + ∀ᶠ s in 𝓝[≠] 1, (deriv riemannZeta s) / (riemannZeta s) + = - (s - 1)⁻¹ + (deriv riemannZeta₁ s) / (riemannZeta₁ s) := by + filter_upwards [eventually_mem_nhdsWithin, + riemannZeta₁_ne_zero_of_near_one.filter_mono nhdsWithin_le_nhds] + grind [deriv_riemannZeta_eq_neg_inv_sub_sq_mul_add, riemannZeta_eq_inv_sub_mul] + +lemma log_deriv_riemannZeta_add_inv_sub_sub_isBigO : + (fun s ↦ (deriv riemannZeta s) / (riemannZeta s) + (s - 1)⁻¹ - γ) + =O[𝓝[≠] 1] (· - 1) := by + suffices (fun s ↦ (deriv riemannZeta₁ s) / (riemannZeta₁ s) - γ) =O[𝓝 1] (· - 1) by + refine (this.mono nhdsWithin_le_nhds).congr' ?_ .rfl + filter_upwards [log_deriv_riemannZeta_eq_neg_inv_sub_add] + simp +contextual + suffices DifferentiableAt ℂ (fun s ↦ (deriv riemannZeta₁ s) / (riemannZeta₁ s)) 1 by + simpa using this.isBigO_sub + fun_prop (disch := simp) + +lemma log_deriv_riemannZeta_add_inv_sub_sub_isLittleO : + (fun s ↦ (deriv riemannZeta s) / (riemannZeta s) + (s - 1)⁻¹ - γ) + =o[𝓝[≠] 1] (fun _ ↦ (1 : ℂ)) := + log_deriv_riemannZeta_add_inv_sub_sub_isBigO.trans_isLittleO + (continuous_id.continuousAt.isLittleO.mono nhdsWithin_le_nhds) + +lemma log_deriv_riemannZeta_add_inv_sub_bounded : + (fun s ↦ (deriv riemannZeta s) / (riemannZeta s) + (s - 1)⁻¹) + =O[𝓝[≠] 1] (fun _ ↦ (1 : ℂ)) := + (isBigO_const_one ..).sub_iff_left.mp log_deriv_riemannZeta_add_inv_sub_sub_isLittleO.isBigO + +lemma inv_riemannZeta_eq_sub_mul : + ∀ᶠ s in 𝓝[≠] 1, (riemannZeta s)⁻¹ = (s - 1) * (riemannZeta₁ s)⁻¹ := by + filter_upwards [eventually_mem_nhdsWithin, + riemannZeta₁_ne_zero_of_near_one.filter_mono nhdsWithin_le_nhds] with s hs + simp [riemannZeta_eq_inv_sub_mul hs, field] + +lemma inv_riemannZeta_sub_sub_isBigO : + (fun s ↦ (riemannZeta s)⁻¹ - (s - 1)) =O[𝓝[≠] 1] (fun s ↦ (s - 1) ^ 2) := by + suffices (fun s ↦ (s - 1) * ((riemannZeta₁ s)⁻¹ - 1)) =O[𝓝 1] (fun s ↦ (s - 1) ^ 2) by + refine (this.mono nhdsWithin_le_nhds).congr' ?_ .rfl + filter_upwards [inv_riemannZeta_eq_sub_mul] + simp +contextual [field] + suffices (fun s ↦ ((riemannZeta₁ s)⁻¹ - 1)) =O[𝓝 1] (· - 1) by + simpa [pow_two] using (isBigO_refl ..).mul this + simpa using ((differentiable_riemannZeta₁.differentiableAt (x := 1)).inv (by simp)).isBigO_sub + +lemma inv_riemannZeta_sub_sub_isLittleO : + (fun s ↦ (riemannZeta s)⁻¹ - (s - 1)) =o[𝓝[≠] 1] (· - 1) := by + apply inv_riemannZeta_sub_sub_isBigO.trans_isLittleO + suffices (· - 1) =o[𝓝 1] (fun _ : ℂ ↦ (1 : ℂ)) by + simpa [pow_two] using (this.mul_isBigO <| isBigO_refl ..).mono nhdsWithin_le_nhds + exact ContinuousAt.isLittleO (by fun_prop) + +lemma inv_riemannZeta_isBigO : + (fun s ↦ (riemannZeta s)⁻¹) =O[𝓝[≠] 1] (· - 1) := + (isBigO_refl ..).sub_iff_left.mp inv_riemannZeta_sub_sub_isLittleO.isBigO + +end near_one diff --git a/Mathlib/NumberTheory/Height/Basic.lean b/Mathlib/NumberTheory/Height/Basic.lean index 7118e99a9d8893..e5bdeb555ef6a8 100644 --- a/Mathlib/NumberTheory/Height/Basic.lean +++ b/Mathlib/NumberTheory/Height/Basic.lean @@ -189,20 +189,20 @@ open Lean.Meta Qq Height /-- Extension for the `positivity` tactic: `Height.mulHeight₁` is always positive. -/ @[positivity Height.mulHeight₁ _] -meta def evalMulHeight₁ : PositivityExt where eval {u α} _ pα? e := do +meta def evalMulHeight₁ : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@mulHeight₁ $K $KF $KA $a) => - let some _ := pα? | pure .none assertInstancesCommute pure (.positive q(mulHeight₁_pos $a)) | _, _, _ => throwError "not Height.mulHeight₁" /-- Extension for the `positivity` tactic: `Height.logHeight₁` is always nonnegative. -/ @[positivity Height.logHeight₁ _] -meta def evalLogHeight₁ : PositivityExt where eval {u α} _ pα? e := do +meta def evalLogHeight₁ : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@logHeight₁ $K $KF $KA $a) => - let some _ := pα? | pure .none assertInstancesCommute pure (.nonnegative q(zero_le_logHeight₁ $a)) | _, _, _ => throwError "not Height.logHeight₁" @@ -510,10 +510,10 @@ open Lean.Meta Qq Height /-- Extension for the `positivity` tactic: `Height.mulHeight` is always positive. -/ @[positivity Height.mulHeight _] -meta def evalMulHeight : PositivityExt where eval {u α} _ pα? e := do +meta def evalMulHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@mulHeight $K $KF $KA $ι $a) => - let some _ := pα? | pure .none -- Check whether there is a `Finite` instance for `$ι` around. match ← trySynthInstanceQ q(Finite $ι) with | .some _instFinite => @@ -524,10 +524,10 @@ meta def evalMulHeight : PositivityExt where eval {u α} _ pα? e := do /-- Extension for the `positivity` tactic: `Height.logHeight` is always nonnegative. -/ @[positivity Height.logHeight _] -meta def evalLogHeight : PositivityExt where eval {u α} _ pα? e := do +meta def evalLogHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@logHeight $K $KF $KA $ι $a) => - let some _ := pα? | pure .none -- Check whether there is a `Finite` instance for `$ι` around. match ← trySynthInstanceQ q(Finite $ι) with | .some _instFinite => diff --git a/Mathlib/NumberTheory/Height/NumberField.lean b/Mathlib/NumberTheory/Height/NumberField.lean index 0e3471a25c812c..852c8132ed30e9 100644 --- a/Mathlib/NumberTheory/Height/NumberField.lean +++ b/Mathlib/NumberTheory/Height/NumberField.lean @@ -5,8 +5,9 @@ Authors: Michael Stoll, Ralf Stephan -/ module -public import Mathlib.NumberTheory.NumberField.ProductFormula public import Mathlib.NumberTheory.Height.Basic +public import Mathlib.NumberTheory.Height.Northcott +public import Mathlib.NumberTheory.NumberField.ProductFormula import Mathlib.Algebra.FiniteSupport.Basic import Mathlib.Algebra.Order.Hom.Lattice @@ -19,6 +20,14 @@ import Mathlib.NumberTheory.NumberField.InfinitePlace.TotallyRealComplex We provide an instance of `Height.AdmissibleAbsValues` for algebraic number fields and set up some API. +## Main results + +* Heights on number fields satisfy the **Northcott property**: If `K` is a number field, + then the set of elements of `K` of bounded (multiplicative or logarithmic) height is finite; + see `NumberField.finite_setOf_mulHeight₁_le` and `NumberField.finite_setOf_logHeight₁_le`. + We also provide instances for `Northcott (mulHeight₁ (K := K))` (which automatically leads + also to `Northcott (logHeight₁ (K := K))`). + ## TODO When this file gets long, split the material on heights over `ℚ` off into a file `Rat.lean`. @@ -96,7 +105,6 @@ lemma sum_nonarchAbsVal_eq {M : Type*} [AddCommMonoid M] (f : AbsoluteValue K (∑ᶠ v : nonarchAbsVal, f v.val) = ∑ᶠ v : FinitePlace K, f v.val := rfl - /-- This is the familiar definition of the multiplicative height on a number field. -/ lemma mulHeight₁_eq (x : K) : mulHeight₁ x = @@ -184,6 +192,218 @@ lemma absNorm_mul_finprod_finitePlace_eq_one (hx : x ≠ 0) : end NumberField +/-! +### The Northcott property for heights on number fields + +We show that a number field `K` has the **Northcott property** with respect to the multiplicative +and with respect to the logarithmic height, i.e., for any `B : ℝ` the set of elements `x : K` +such that `mulHeight₁ x ≤ B` (resp., `logHeight₁ x ≤ B`) is finite. +See `NumberField.finite_setOf_mulHeight₁_le` and `NumberField.finite_setOf_logHeight₁_le`. + +The main idea of the proof is as follows. We show that for every `x : K` there is `n : ℕ` such that +`n * x` is an algebraic integer and `n ≤ mulHeight₁ x`; see `NumberField.exists_nat_le_mulHeight₁`. +We also show that the set of `a : 𝓞 K` such that `mulHeight₁ (a / n)` is bounded is finite; +see `NumberField.finite_setOf_prod_infinitePlace_iSup_le`. The result for the multiplicative height +follows by combining these two ingredients, and the result for the logarithmic height follows +from that for any field with a family of admissible absolute values +(see `Mathlib.NumberTheory.Height.Northcott`). +-/ + +section Northcott + +namespace NumberField + +variable {K : Type*} [Field K] [NumberField K] + +section withIdeal + +open Ideal + +private lemma relIndex_span_span_nat_mul (m : ℕ) {n : ℕ} (hn : n ≠ 0) (a : 𝓞 K) : + (span {(m : 𝓞 K)}).toAddSubgroup.relIndex (span {↑m, a}).toAddSubgroup = + (span {(n * m : 𝓞 K)}).toAddSubgroup.relIndex (span {↑(n * m), n * a}).toAddSubgroup := by + let f : 𝓞 K →ₗ[𝓞 K] 𝓞 K := .mulLeft _ n + have hf : Function.Injective (f : 𝓞 K →+ 𝓞 K) := + (injective_iff_map_eq_zero f).mpr fun _ _ ↦ by simp_all [f] + have H₁ : span {(n * m : 𝓞 K)} = Submodule.map f (span {↑m}) := by + simp [LinearMap.map_span, f] + have H₂ : span {↑(n * m), n * a} = Submodule.map f (span {↑m, a}) := by + simp [LinearMap.map_span, f, Set.image_pair] + rw [H₁, H₂] + exact AddSubgroup.relIndex_map_map_of_injective _ _ hf |>.symm + +private lemma relIndex_span_span_eq_relIndex_span_span {m n : ℕ} (hm : m ≠ 0) (hn : n ≠ 0) + {a b : 𝓞 K} (h : n * a = m * b) : + (span {(m : 𝓞 K)}).toAddSubgroup.relIndex (span {↑m, a}).toAddSubgroup = + (span {(n : 𝓞 K)}).toAddSubgroup.relIndex (span {↑n, b}).toAddSubgroup := by + refine (relIndex_span_span_nat_mul m hn a).trans ?_ + rw [mul_comm, mul_comm n, h] + exact (relIndex_span_span_nat_mul n hm b).symm + +open Module AddSubgroup LinearMap in +lemma exists_nat_ne_zero_exists_integer_mul_eq_and_absNorm_span_eq_pow (x : K) : + ∃ n : ℕ, n ≠ 0 ∧ ∃ a : 𝓞 K, n * x = a ∧ + (span {(n : 𝓞 K), a}).absNorm = n ^ (Module.finrank ℚ K - 1) := by + have hx : IsAlgebraic ℤ x := IsFractionRing.isAlgebraic_iff ℤ _ _ |>.mpr (.of_finite ℚ x) + obtain ⟨m, r, hm, hmr⟩ := hx.exists_nsmul_eq (𝓞 K) + rw [← RingOfIntegers.coe_eq_algebraMap r] at hmr + set n := (span {(m : 𝓞 K)}).toAddSubgroup.relIndex (span {(m : 𝓞 K), r}).toAddSubgroup with hndef + have hn : n ≠ 0 := isFiniteRelIndex (by simp [hm]) _ |>.relIndex_ne_zero + obtain ⟨a, ha'⟩ : ∃ a, m * a = n * r := by + have : n • r ∈ span {(m : 𝓞 K)} := + (span {(m : 𝓞 K)}).toAddSubgroup.nsmul_relIndex_mem <| Submodule.mem_span_of_mem <| by grind + simpa [mem_span_singleton', mul_comm] using this + have ha : n * x = a := by + refine mul_left_cancel₀ (mod_cast hm : (m : K) ≠ 0) ?_ + rw [mul_left_comm, ← nsmul_eq_mul m, hmr] + exact_mod_cast ha'.symm + refine ⟨n, hn, a, ha, mul_left_cancel₀ hn ?_⟩ + nth_rewrite 1 [hndef] + rw [absNorm_eq_index, mul_pow_sub_one finrank_pos.ne', ← RingOfIntegers.rank, + ← absNorm_span_natCast, absNorm_eq_index, ← relIndex_span_span_eq_relIndex_span_span hn hm ha'] + exact relIndex_mul_index <| Submodule.toAddSubgroup_mono <| span_mono <| by grind + +open Height in +private lemma one_le_pow_totalWeight_mul_finprod {n : ℕ} (hn : n ≠ 0) (a : 𝓞 K) : + 1 ≤ (n ^ totalWeight K : ℝ) * ∏ᶠ (v : FinitePlace K), ⨆ i, v (![↑a, ↑n] i) := by + have Hw : (0 : ℝ) < n ^ totalWeight K := by positivity + rw_mod_cast [totalWeight_eq_finrank, ← RingOfIntegers.rank, ← absNorm_span_natCast] at Hw ⊢ + rw [← absNorm_mul_finprod_finitePlace_eq_one (show ![a, n] ≠ 0 by simp [hn])] + gcongr + · exact finprod_nonneg fun _ ↦ Real.iSup_nonneg_of_nonnegHomClass .. + · exact Nat.le_of_dvd Hw <| absNorm_dvd_absNorm_of_le <| span_mono <| by simp + · apply le_of_eq; congr; ext; congr; ext i; fin_cases i <;> simp + +end withIdeal + +open Height + +section withFinset + +open Finset + +/-- If `x : K` (for a number field `K`), then we can find a nonzero `n : ℕ` such that +`n ≤ mulHeight₁ x` and `n * x` is integral. I.e., the denominator of `x` can be bounded by +its multplicative height. -/ +-- TODO: Use this to show `natDenominator x ≤ mulHeight₁ x` once #39872 is merged. +lemma exists_nat_le_mulHeight₁ (x : K) : + ∃ n : ℕ, n ≠ 0 ∧ n ≤ mulHeight₁ x ∧ IsIntegral ℤ (n * x) := by + obtain ⟨n, hn, a, ha₁, ha₂⟩ := exists_nat_ne_zero_exists_integer_mul_eq_and_absNorm_span_eq_pow x + refine ⟨n, hn, ?_, ha₁ ▸ a.isIntegral_coe⟩ + rw [← totalWeight_eq_finrank] at ha₂ + have hv (i : Fin 2) : (![a, n] i : K) = ![(a : K), n] i := by fin_cases i <;> rfl + rw [← mul_div_cancel_left₀ x (mod_cast hn : (n : K) ≠ 0), ha₁, mulHeight₁_div_eq_mulHeight, + mulHeight_eq (by simp [hn])] + refine le_of_mul_le_mul_left ?_ (show (0 : ℝ) < n ^ (totalWeight K - 1) by positivity) + have : n ^ (totalWeight K - 1) * ∏ᶠ (v : FinitePlace K), ⨆ i, v (![(a : K), n] i) = 1 := by + simpa [ha₂, hv] using absNorm_mul_finprod_finitePlace_eq_one (show ![a, n] ≠ 0 by simp [hn]) + rw [pow_sub_one_mul (totalWeight_pos K).ne', mul_left_comm, this, mul_one, + totalWeight_eq_sum_mult, ← prod_pow_eq_pow_sum univ] + gcongr + exact Finite.le_ciSup_of_le 1 <| by simp + +private lemma pow_totalWeight_sub_one_eq [DecidableEq (InfinitePlace K)] {n : ℕ} (hn : n ≠ 0) + (v : InfinitePlace K) : + (n ^ (totalWeight K - 1) : ℝ) = (∏ w ∈ univ.erase v, (n ^ w.mult : ℝ)) * n ^ (v.mult - 1) := by + refine mul_right_cancel₀ (b := (n : ℝ)) (mod_cast hn) ?_ + rw [pow_sub_one_mul (totalWeight_pos K).ne', totalWeight_eq_sum_mult, ← prod_pow_eq_pow_sum, + ← prod_erase_mul _ _ (mem_univ v), ← pow_sub_one_mul v.mult_ne_zero, ← mul_assoc] + +private lemma infinitePlace_apply_le_of_prod_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) {x : 𝓞 K} + (h : ∏ v : InfinitePlace K, (⨆ i, v (![(x : K), n] i)) ^ v.mult ≤ B) (v : InfinitePlace K) : + v x ≤ B / n ^ (totalWeight K - 1) := by + classical + rw [le_div_iff₀' (by positivity)] + calc + _ ≤ n ^ (totalWeight K - 1) * ⨆ i, v (![(x : K), n] i) := by + gcongr; exact Finite.le_ciSup_of_le 0 le_rfl + _ ≤ (∏ v' ∈ univ.erase v, (⨆ i, v' (![↑x, ↑n] i)) ^ v'.mult) * + (⨆ i, v (![↑x, ↑n] i)) ^ (v.mult - 1) * ⨆ i, v (![(x : K), n] i) := by + rw [pow_totalWeight_sub_one_eq hn] + gcongr + · exact Real.iSup_nonneg_of_nonnegHomClass .. + · exact prod_nonneg fun _ _ ↦ pow_nonneg (Real.iSup_nonneg_of_nonnegHomClass ..) _ + all_goals exact Finite.le_ciSup_of_le 1 <| by simp + _ ≤ B := by + rwa [mul_assoc, pow_sub_one_mul v.mult_ne_zero, prod_erase_mul _ _ (mem_univ v)] + +end withFinset + +lemma finite_setOf_prod_infinitePlace_iSup_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) : + {x : 𝓞 K | ∏ v : InfinitePlace K, (⨆ i, v (![(x : K), n] i)) ^ v.mult ≤ B}.Finite := by + set B' := B / n ^ (totalWeight K - 1) + suffices Set.BijOn ((↑) : 𝓞 K → K) {x | ∀ (v : InfinitePlace K), v x ≤ B'} + {x | IsIntegral ℤ x ∧ ∀ (φ : K →+* ℂ), ‖φ x‖ ≤ B'} from + this.finite_iff_finite.mpr (Embeddings.finite_of_norm_le K ℂ B') |>.subset + fun _ _ ↦ by grind [infinitePlace_apply_le_of_prod_le hn B] + refine .mk (fun x hx ↦ ?_) (fun _ _ _ _ ↦ RingOfIntegers.ext) fun a ha ↦ ?_ <;> + simp only [Set.mem_image, Set.mem_setOf_eq] at * + · exact ⟨x.isIntegral_coe, fun φ ↦ hx <| .mk φ⟩ + · rw [← mem_integralClosure_iff ℤ K] at ha + exact ⟨⟨a, ha.1⟩, fun v ↦ v.norm_embedding_eq a ▸ ha.2 v.embedding, rfl⟩ + +/-- The set of `a : 𝓞 K` such that `mulHeight₁ (a / n) = mulHeight ![a, n]` is bounded +(for some given nonzero `n : ℕ`) is finite. -/ +lemma finite_setOf_mulHeight_nat_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) : + {a : 𝓞 K | mulHeight ![(a : K), n] ≤ B}.Finite := by + suffices {a : 𝓞 K | mulHeight ![(a : K), n] ≤ B} ⊆ + {a | ∏ v : InfinitePlace K, (⨆ i, v (![(a : K), n] i)) ^ v.mult ≤ n ^ totalWeight K * B} from + (finite_setOf_prod_infinitePlace_iSup_le hn _).subset this + refine Set.setOf_subset_setOf_of_imp fun a ha ↦ ?_ + rw [mulHeight_eq <| by simp [hn], mul_comm] at ha + grw [← ha, ← mul_assoc, ← one_le_pow_totalWeight_mul_finprod hn, one_mul] + -- nonnegativity side goal + exact Finset.prod_nonneg fun _ _ ↦ pow_nonneg (Real.iSup_nonneg_of_nonnegHomClass ..) _ + +variable (K) in +/- The set of `x : K` such that `mulHeight₁ x` is bounded and `n * x` is integral +(for some given nonzero `n : ℕ`) is finite. +This is a stepping stone for the proof of the next result, which is strictly stronger. -/ +private lemma finite_setOf_isIntegral_nat_mul_and_mulHeight₁_le {n : ℕ} (hn : n ≠ 0) (B : ℝ) : + {x : K | IsIntegral ℤ (n * x) ∧ mulHeight₁ x ≤ B}.Finite := by + have hn' : (n : K) ≠ 0 := mod_cast hn + suffices Set.BijOn (fun a : 𝓞 K ↦ (a / n : K)) {a | mulHeight ![(a : K), n] ≤ B} + {x | IsIntegral ℤ (n * x) ∧ mulHeight₁ x ≤ B} from + this.finite_iff_finite.mp <| finite_setOf_mulHeight_nat_le hn B + refine .mk (fun a ha ↦ ?_) (fun a _ b _ h ↦ ?_) fun x ⟨hx₁, hx₂⟩ ↦ ?_ + · simp only [Set.mem_setOf_eq] at ha ⊢ + rw [mul_div_cancel₀ (a : K) hn', mulHeight₁_div_eq_mulHeight] + exact ⟨a.isIntegral_coe, ha⟩ + · rwa [div_left_inj' hn', RingOfIntegers.eq_iff] at h + · simp only [Set.mem_setOf_eq, Set.mem_image] + obtain ⟨a, ha⟩ : ∃ a : 𝓞 K, n * x = a := ⟨⟨_, hx₁⟩, rfl⟩ + refine ⟨a, ?_, (EuclideanDomain.eq_div_of_mul_eq_right hn' ha).symm⟩ + rwa [← ha, ← mulHeight₁_div_eq_mulHeight, mul_div_cancel_left₀ x hn'] + +variable (K) in +/-- A number field `K` satisfies the **Northcott property**: +The set of elements of bounded multiplicative height is finite. -/ +theorem finite_setOf_mulHeight₁_le (B : ℝ) : {x : K | mulHeight₁ x ≤ B}.Finite := by + have H : {x : K | mulHeight₁ x ≤ B} = + ⋃ n : Fin ⌊B⌋₊, {x : K | IsIntegral ℤ ((n + 1) * x) ∧ mulHeight₁ x ≤ B} := by + ext x : 1 + obtain ⟨n, hn₀, hn₁, hn⟩ := exists_nat_le_mulHeight₁ x + simp only [Set.mem_setOf_eq, Set.mem_iUnion, exists_and_right, iff_and_self] + refine fun h ↦ ⟨⟨n - 1, by grind [Nat.le_floor <| hn₁.trans h]⟩, ?_⟩ + rwa [← Nat.cast_add_one, Nat.sub_one_add_one hn₀] + rw [H] + exact Set.finite_iUnion fun n ↦ + mod_cast finite_setOf_isIntegral_nat_mul_and_mulHeight₁_le K (Nat.zero_ne_add_one n).symm B + +instance : Northcott (mulHeight₁ (K := K)) where + finite_le := finite_setOf_mulHeight₁_le K + +variable (K) in +/-- A number field `K` satisfies the **Northcott property**: +The set of elements of bounded logarithmic height is finite. -/ +theorem finite_setOf_logHeight₁_le (B : ℝ) : + {x : K | logHeight₁ x ≤ B}.Finite := + Northcott.finite_le B + +end NumberField + +end Northcott + /-! ### Positivity extension for totalWeight on number fields -/ @@ -194,10 +414,10 @@ open Lean.Meta Qq /-- Extension for the `positivity` tactic: `Height.totalWeight` is positive for number fields. -/ @[positivity Height.totalWeight _] -meta def evalHeightTotalWeight : PositivityExt where eval {u α} _ pα? e := do +meta def evalHeightTotalWeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(@Height.totalWeight $K $KF $KA) => - let some _ := pα? | pure .none -- Check whether there is a `NumberField` instance for `$K` around. match ← trySynthInstanceQ q(NumberField $K) with | .some _inst => diff --git a/Mathlib/NumberTheory/Height/Projectivization.lean b/Mathlib/NumberTheory/Height/Projectivization.lean index 0caa26f4437275..6d7c19f22bc3e6 100644 --- a/Mathlib/NumberTheory/Height/Projectivization.lean +++ b/Mathlib/NumberTheory/Height/Projectivization.lean @@ -83,20 +83,20 @@ open Lean.Meta Qq Projectivization /-- Extension for the `positivity` tactic: `Projectivization.mulHeight` is always positive. -/ @[positivity Projectivization.mulHeight _] -meta def evalProjMulHeight : PositivityExt where eval {u α} _ pα? e := do +meta def evalProjMulHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@mulHeight $K $KF $KA $ι $ιF $a) => - let some _ := pα? | pure .none assertInstancesCommute pure (.positive q(mulHeight_pos $a)) | _, _, _ => throwError "not Projectivization.mulHeight" /-- Extension for the `positivity` tactic: `Projectivization.logHeight` is always nonnegative. -/ @[positivity Projectivization.logHeight _] -meta def evalProjLogHeight : PositivityExt where eval {u α} _ pα? e := do +meta def evalProjLogHeight : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@logHeight $K $KF $KA $ι $ιF $a) => - let some _ := pα? | pure .none assertInstancesCommute pure (.nonnegative q(logHeight_nonneg $a)) | _, _, _ => throwError "not Projectivization.logHeight" diff --git a/Mathlib/NumberTheory/KummerDedekind.lean b/Mathlib/NumberTheory/KummerDedekind.lean index a295f1c42605fb..047f50d41348f1 100644 --- a/Mathlib/NumberTheory/KummerDedekind.lean +++ b/Mathlib/NumberTheory/KummerDedekind.lean @@ -205,8 +205,6 @@ theorem Ideal.irreducible_map_of_irreducible_minpoly (hI : IsMaximal I) (hI' : I rw [Multiset.attach_map_val, Multiset.map_singleton, Subtype.coe_mk] exact normalizedFactors_irreducible hf -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in open Set Classical in /-- 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 @@ -218,19 +216,19 @@ theorem normalizedFactorsMapEquivNormalizedFactorsMinPolyMk_symm_apply_eq_span (hI' : I ≠ ⊥) (hx : (conductor R x).comap (algebraMap R S) ⊔ I = ⊤) (hx' : IsIntegral R x) : ((normalizedFactorsMapEquivNormalizedFactorsMinPolyMk hI hI' hx hx').symm ⟨_, hQ⟩).val = span (I.map (algebraMap R S) ∪ {Q.aeval x}) := by - dsimp [normalizedFactorsMapEquivNormalizedFactorsMinPolyMk, - Ideal.normalizedFactorsEquivSpanNormalizedFactors] - rw [IsDedekindDomain.normalizedFactorsEquivOfQuotEquiv_symm] - dsimp [IsDedekindDomain.normalizedFactorsEquivOfQuotEquiv, - IsDedekindDomain.idealFactorsEquivOfQuotEquiv, OrderIso.ofHomInv] - simp only [map_span, image_singleton, coe_coe, quotMapEquivQuotQuotMap_symm_apply hx hx' Q] - refine le_antisymm (fun a ha ↦ ?_) (span_le.mpr <| union_subset_iff.mpr <| - ⟨le_comap_of_map_le (by simp), by simp⟩) - rw [mem_comap, Ideal.mem_span_singleton] at ha - obtain ⟨a', ha'⟩ := ha - obtain ⟨b, hb⟩ := Ideal.Quotient.mk_surjective a' - rw [← hb, ← map_mul, Quotient.mk_eq_mk_iff_sub_mem] at ha' - rw [union_comm, span_union, span_eq, mem_span_singleton_sup] - exact ⟨b, a - Q.aeval x * b, ha', by ring⟩ + unfold normalizedFactorsMapEquivNormalizedFactorsMinPolyMk + Ideal.normalizedFactorsEquivSpanNormalizedFactors + rw [Equiv.symm_trans_apply, IsDedekindDomain.normalizedFactorsEquivOfQuotEquiv_symm] + unfold IsDedekindDomain.normalizedFactorsEquivOfQuotEquiv + rw [Equiv.coe_fn_mk, Equiv.symm_symm, Equiv.ofBijective_apply] + dsimp only + unfold IsDedekindDomain.idealFactorsEquivOfQuotEquiv + rw [OrderIso.ofHomInv_apply] + erw [IsDedekindDomain.idealFactorsFunOfQuotHom_coe_coe] + dsimp only + rw [map_span, image_singleton, map_span, image_singleton, coe_coe, + quotMapEquivQuotQuotMap_symm_apply, span_union, span_eq, sup_comm, + ← image_singleton, ← map_span, Ideal.comap_map_of_surjective' _ Ideal.Quotient.mk_surjective, + Ideal.mk_ker] end KummerDedekind diff --git a/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean b/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean index 2d095f687b1e24..947ea4773befb0 100644 --- a/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean +++ b/Mathlib/NumberTheory/LSeries/DirichletContinuation.lean @@ -390,7 +390,7 @@ lemma continuousOn_neg_logDeriv_LFunction_of_nontriv (hχ : χ ≠ 1) : ContinuousOn (fun s ↦ -deriv (LFunction χ) s / LFunction χ s) {s | LFunction χ s ≠ 0} := by have h := differentiable_LFunction hχ simpa [neg_div] using! ((h.contDiff.continuous_deriv le_rfl).continuousOn.div - h.continuous.continuousOn fun _ hw ↦ hw).neg + h.continuous.continuousOn fun _ hw ↦ hw).fun_neg end nontrivial diff --git a/Mathlib/NumberTheory/LSeries/RiemannZeta.lean b/Mathlib/NumberTheory/LSeries/RiemannZeta.lean index fdff2c94adcfd8..3c92195fce20df 100644 --- a/Mathlib/NumberTheory/LSeries/RiemannZeta.lean +++ b/Mathlib/NumberTheory/LSeries/RiemannZeta.lean @@ -117,6 +117,7 @@ lemma completedRiemannZeta_residue_one : -/ /-- The Riemann zeta function `ζ(s)`. -/ +@[wikidata Q187235] def riemannZeta := hurwitzZetaEven 0 lemma HurwitzZeta.hurwitzZetaEven_zero : hurwitzZetaEven 0 = riemannZeta := rfl diff --git a/Mathlib/NumberTheory/LucasLehmer.lean b/Mathlib/NumberTheory/LucasLehmer.lean index cfac67c69e8e3b..137d17b82b52ba 100644 --- a/Mathlib/NumberTheory/LucasLehmer.lean +++ b/Mathlib/NumberTheory/LucasLehmer.lean @@ -78,8 +78,8 @@ alias ⟨_, mersenne_pos_of_pos⟩ := mersenne_pos /-- Extension for the `positivity` tactic: `mersenne`. -/ @[positivity mersenne _] -meta def evalMersenne : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | pure .none +meta def evalMersenne : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(mersenne $a) => assertInstancesCommute diff --git a/Mathlib/NumberTheory/ModularForms/Derivative.lean b/Mathlib/NumberTheory/ModularForms/Derivative.lean index e7f4df6c11143a..4fca1821ea6dde 100644 --- a/Mathlib/NumberTheory/ModularForms/Derivative.lean +++ b/Mathlib/NumberTheory/ModularForms/Derivative.lean @@ -6,12 +6,19 @@ Authors: Seewoo Lee module public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.E2.MDifferentiable +public import Mathlib.NumberTheory.ModularForms.EisensteinSeries.E2.Transform /-! # Derivatives of modular forms This file defines normalized derivative $D = \frac{1}{2\pi i} \frac{d}{dz}$ -and serre dervative $\partial_k := D - \frac{k}{12} E_2$ of modular forms. +and (Ramanujan-)Serre derivative $\partial_k := D - \frac{k}{12} E_2$ of modular forms. + +## Main Definitions and Theorems + +- `normalizedDerivOfComplex`: $D = \frac{1}{2\pi i} \frac{d}{dz}$ +- `serreDerivative`: $\partial_k F := D F - \frac{k}{12} E_2 F$ +- `serreDerivative_slash_equivariant`: Serre derivative is equivariant under the slash action. TODO: - Serre derivative preserves modularity, i.e. $\partial_k (M_k) \subseteq M_{k+2}$. @@ -21,7 +28,7 @@ TODO: open UpperHalfPlane hiding I open Real Complex -open scoped Manifold +open scoped Manifold MatrixGroups ModularForm Topology namespace Derivative @@ -151,8 +158,8 @@ theorem serreDerivative_smul (k : ℂ) (c : ℂ) (F : ℍ → ℂ) (hF : MDiff F ring_nf theorem serreDerivative_mul (k₁ k₂ : ℂ) (F G : ℍ → ℂ) (hF : MDiff F) (hG : MDiff G) : - serreDerivative (k₁ + k₂) (F * G) = (serreDerivative k₁ F) * G + F * (serreDerivative k₂ G) - := by + serreDerivative (k₁ + k₂) (F * G) = + (serreDerivative k₁ F) * G + F * (serreDerivative k₂ G) := by ext z simp [serreDerivative, normalizedDerivOfComplex_mul F G hF hG] ring_nf @@ -169,6 +176,78 @@ theorem serreDerivative_mdifferentiable {F : ℍ → ℂ} (k : ℂ) (hF : MDiff MDiff (fun z ↦ (k * 12⁻¹) * (EisensteinSeries.E2 z * F z))) simp [Pi.mul_apply, mul_assoc, mul_left_comm, mul_comm] +open ModularGroup + +/-- How `D` interacts with the slash action. -/ +lemma normalizedDerivOfComplex_slash {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F) + {g : GL (Fin 2) ℝ} (hg : 0 < g.val.det) : + D (F ∣[k] g) = fun z : ℍ ↦ (g.val.det : ℂ)⁻¹ * (D F ∣[k + 2] g) z - + (k : ℂ) * (2 * π * I)⁻¹ * (g 1 0 / denom g z) * (F ∣[k] g) z := by + have hdet : g.det.val = g.val.det := Matrix.GeneralLinearGroup.val_det_apply g + have hdetℂ : (g.val.det : ℂ) ≠ 0 := Complex.ofReal_ne_zero.mpr hg.ne' + have hσ (x) : σ g x = x := by grind [σ, ContinuousAlgEquiv.refl_apply] + ext z + simp only [normalizedDerivOfComplex, ModularForm.slash_apply] + have hz := denom_ne_zero g z + have h_smul : HasDerivAt (fun w ↦ ↑(g • ofComplex w) : ℂ → ℂ) + ((g.val.det : ℂ) / denom g z ^ 2) ↑z := (hasStrictDerivAt_smul hg z).hasDerivAt + have h_F : HasDerivAt (F ∘ ofComplex) (deriv (F ∘ ofComplex) ↑(g • ofComplex (z : ℂ))) + ↑(g • ofComplex (z : ℂ)) := + (ofComplex_apply z).symm ▸ (mdifferentiableAt_iff.mp (hF (g • z))).hasDerivAt + have h_denom : HasDerivAt (fun w ↦ (denom g w) ^ (-k)) + (-k * (g 1 0 : ℂ) * (denom g z) ^ (-k - 1)) ↑z := by + simpa using hasDerivAt_denom_zpow g (-k) z + have hcomp : ((F ∣[k] g) ∘ ofComplex) =ᶠ[𝓝 ↑z] + fun w ↦ (g.val.det : ℂ) ^ (k - 1) * + ((F ∘ ofComplex) ↑(g • ofComplex w) * (denom g w) ^ (-k)) := by + filter_upwards [isOpen_upperHalfPlaneSet.mem_nhds z.im_pos] with w hw + grind [ofComplex_apply_of_im_pos, ofComplex_apply, ModularForm.slash_apply] + rw [((((h_F.comp (z : ℂ) h_smul).mul h_denom).const_mul _).congr_of_eventuallyEq hcomp).deriv] + simp only [hσ, hdet, abs_of_pos hg, ofComplex_apply, Function.comp_apply] + rw [show k + 2 - 1 = (k - 1) + 2 by ring, show -(k + 2) = -k + -2 by ring, + zpow_add₀ hdetℂ, zpow_add₀ hz, zpow_sub_one₀ hz] + field + +/-- The `SL(2, ℤ)` case of `normalizedDerivOfComplex_slash`, where the determinant factor is `1`. -/ +lemma normalizedDerivOfComplex_SL_slash {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F) {γ : SL(2, ℤ)} : + D (F ∣[k] γ) = (D F ∣[k + 2] γ) - + (fun z : ℍ ↦ (k : ℂ) * (2 * π * I)⁻¹ * (γ 1 0 / denom γ z) * (F ∣[k] γ) z) := by + have hdet : (γ : GL (Fin 2) ℝ).val.det = 1 := by + rw [← Matrix.GeneralLinearGroup.val_det_apply]; simp + ext z + have := congrFun + (normalizedDerivOfComplex_slash (k := k) hF (g := (γ : GL (Fin 2) ℝ)) (by grind)) z + rw [hdet] at this + simpa [ModularForm.SL_slash] using this + +/-- +Serre derivative is equivariant under the slash action. More precisely, +$\partial_k (F ∣[k] γ) = (\partial_k F) ∣[k + 2] \gamma$ for all $\gamma \in SL(2, \mathbb{Z})$. +-/ +theorem serreDerivative_slash_equivariant {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F) {γ : SL(2, ℤ)} : + serreDerivative k F ∣[k + 2] γ = serreDerivative k (F ∣[k] γ) := by + ext z + have hLHS : (serreDerivative (k : ℂ) F ∣[k + 2] γ) z = + (D F ∣[k + 2] γ) z - ↑k * 12⁻¹ * ((EisensteinSeries.E2 ∣[(2 : ℤ)] γ) z * (F ∣[k] γ) z) := by + grind [ModularForm.SL_slash_apply, serreDerivative_apply, Pi.mul_apply, + congrFun (ModularForm.mul_slash_SL2 2 k γ EisensteinSeries.E2 F) z] + have hDz : (D (F ∣[k] γ)) z = (D F ∣[k + 2] γ) z - + (k * (2 * π * I)⁻¹ * (γ 1 0 / denom γ z) * (F ∣[k] γ) z) := by + simp [normalizedDerivOfComplex_SL_slash hF] + have hE2z : (EisensteinSeries.E2 ∣[(2 : ℤ)] γ) z = + EisensteinSeries.E2 z - 1 / (2 * riemannZeta 2) * EisensteinSeries.D2 γ z := by + simp [EisensteinSeries.E2_slash_action] + grind [serreDerivative_apply, EisensteinSeries.D2, riemannZeta_two, I_sq] + +/-- +As a corollary, if `F` is invariant under the slash action of weight `k`, then +`serreDerivative k F` is invariant under the slash action of weight `k + 2`. +-/ +theorem serreDerivative_slash_invariant {k : ℤ} {F : ℍ → ℂ} (hF : MDiff F) {γ : SL(2, ℤ)} + (h : F ∣[k] γ = F) : + serreDerivative k F ∣[k + 2] γ = serreDerivative k F := by + grind [serreDerivative_slash_equivariant] + end end Derivative diff --git a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/E2/Summable.lean b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/E2/Summable.lean index 7272e6e7eae428..76fb2308303e97 100644 --- a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/E2/Summable.lean +++ b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/E2/Summable.lean @@ -103,6 +103,23 @@ lemma E2_eq_tsum_cexp : E2 z = 1 - 24 * ∑' n : ℕ+, σ 1 n * 𝕢 z ^ (n : simp [E2, G2_eq_tsum_cexp, riemannZeta_two] field +/-- The `q`-expansion of `E2` as a `HasSum` over `ℕ`. -/ +theorem hasSum_qExpansion_E2 : + HasSum (fun m : ℕ ↦ (if m = 0 then 1 else -24 * σ 1 m : ℂ) • 𝕢 z ^ m) (E2 z) := by + have hS : Summable fun n : ℕ ↦ σ 1 (n + 1) * 𝕢 z ^ (n + 1) := + (summable_nat_add_iff 1).mpr (summable_sigma_mul_cexp_pow (k := 2) (one_le_two) z) + rw [← hasSum_nat_add_iff' 1] + convert! (hS.mul_left (-24)).hasSum using 1 + · ext : 1 + simp [mul_assoc] + · rw [E2_eq_tsum_cexp, tsum_pnat_eq_tsum_succ (f := fun n ↦ σ 1 n * 𝕢 z ^ n), tsum_mul_left] + simp + +/-- `E2` is bounded at `i∞`. -/ +theorem isBoundedAtImInfty_E2 : IsBoundedAtImInfty E2 := + isBoundedAtImInfty_of_hasSum_qExpansion one_pos fun τ ↦ by + simpa only [Function.Periodic.qParam, ofReal_one, div_one] using hasSum_qExpansion_E2 (z := τ) + lemma tendsto_e2Summand_atTop_nhds_zero : Tendsto (e2Summand · z) atTop (𝓝 0) := (summable_e2Summand_symmetricIcc z).tendsto_zero_of_even_summable_symmetricIcc (e2Summand_even _) diff --git a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean index 62fdc501c49be4..907535d57c87cc 100644 --- a/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean +++ b/Mathlib/NumberTheory/ModularForms/EisensteinSeries/QExpansion.lean @@ -308,7 +308,7 @@ open ModularFormClass local notation "𝕢" => Periodic.qParam /-- Summability of the divisor-sum q-expansion series `∑ σ_{k-1}(n) q^n`. -/ -private lemma summable_sigma_mul_cexp_pow {k : ℕ} (hk : 1 ≤ k) (z : ℍ) : +lemma EisensteinSeries.summable_sigma_mul_cexp_pow {k : ℕ} (hk : 1 ≤ k) (z : ℍ) : Summable fun n : ℕ ↦ (σ (k - 1) n : ℂ) * cexp (2 * π * I * z) ^ n := by apply Summable.of_norm_bounded (summable_norm_pow_mul_geometric_of_norm_lt_one k (norm_exp_two_pi_I_lt_one z)) diff --git a/Mathlib/NumberTheory/ModularForms/QExpansion.lean b/Mathlib/NumberTheory/ModularForms/QExpansion.lean index 8b0df914943f08..cddcd9165a1348 100644 --- a/Mathlib/NumberTheory/ModularForms/QExpansion.lean +++ b/Mathlib/NumberTheory/ModularForms/QExpansion.lean @@ -189,8 +189,7 @@ lemma hasSum_qExpansion_of_norm_lt {f : ℍ → ℂ} (hh : 0 < h) lemma hasSum_qExpansion {f : ℍ → ℂ} (hh : 0 < h) (hfper : Periodic (f ∘ ofComplex) h) (hfhol : MDiff f) (hfbdd : IsBoundedAtImInfty f) (τ : ℍ) : HasSum (fun m : ℕ ↦ (qExpansion h f).coeff m • 𝕢 h τ ^ m) (f τ) := by - have : 0 < 2 * π * τ.im / h := by positivity - have : ‖𝕢 h τ‖ < 1 := by simpa [Periodic.qParam, Complex.norm_exp, neg_div] + have : ‖𝕢 h τ‖ < 1 := Periodic.norm_qParam_lt_one hh τ.im_pos simpa [eq_cuspFunction τ hh.ne' hfper] using hasSum_qExpansion_of_norm_lt hh hfper hfhol hfbdd this @@ -258,6 +257,25 @@ private lemma hasFPowerSeriesOnBall_update {f : ℍ → ℂ} (hh : 0 < h) {c : · simpa [update_of_ne hy', mul_comm] using hasSum_cuspFunction_of_hasSum_punctured hh hf hy hy' +/-- A function on the upper half plane that is given everywhere by a convergent `q`-expansion with +non-negative exponents, `f τ = ∑' m, c m * 𝕢 h τ ^ m`, is bounded at `i∞`. This is a converse to +`hasSum_qExpansion`: there, boundedness is a hypothesis used to produce the `q`-expansion, while +here convergence of the `q`-expansion is enough to deduce boundedness. -/ +theorem isBoundedAtImInfty_of_hasSum_qExpansion {f : ℍ → ℂ} {c : ℕ → ℂ} (hh : 0 < h) + (hf : ∀ τ : ℍ, HasSum (fun m ↦ c m • 𝕢 h τ ^ m) (f τ)) : IsBoundedAtImInfty f := by + have hfeq : f = fun τ : ℍ ↦ update (cuspFunction h f) 0 (c 0) (𝕢 h τ) := by + funext τ + rw [update_of_ne (Periodic.qParam_ne_zero _)] + exact (hf τ).unique (hasSum_cuspFunction_of_hasSum_punctured hh hf + (Periodic.norm_qParam_lt_one hh τ.im_pos) (exp_ne_zero _)) + have htend : Tendsto f atImInfty (𝓝 (c 0)) := by + rw [hfeq] + simpa [update_self, Function.comp_def] using + (hasFPowerSeriesOnBall_update hh hf).hasFPowerSeriesAt.continuousAt.tendsto.comp + (qParam_tendsto_atImInfty hh) + -- `IsBoundedAtImInfty f = BoundedAtFilter atImInfty f = (f =O[atImInfty] 1)` by definition. + exact htend.isBigO_one ℝ + lemma hasFPowerSeriesOnBall_cuspFunction {f : ℍ → ℂ} {c : ℕ → ℂ} (hh : 0 < h) (hfanalytic : AnalyticAt ℂ (cuspFunction h f) 0) (hf : ∀ τ : ℍ, HasSum (fun m ↦ c m • 𝕢 h τ ^ m) (f τ)) : diff --git a/Mathlib/NumberTheory/MulChar/Lemmas.lean b/Mathlib/NumberTheory/MulChar/Lemmas.lean index 1f2af16ecf0116..a12ad1c2c010eb 100644 --- a/Mathlib/NumberTheory/MulChar/Lemmas.lean +++ b/Mathlib/NumberTheory/MulChar/Lemmas.lean @@ -51,11 +51,10 @@ instance instStarMul [StarRing R'] : StarMul (MulChar R R') where star := starComp star_involutive χ := by ext1 - simp only [starComp_apply, RingHomCompTriple.comp_apply, RingHom.id_apply] + simp [starComp_apply] star_mul χ χ' := by ext1 - simp only [starComp_apply, starRingEnd, coeToFun_mul, Pi.mul_apply, map_mul, RingHom.coe_coe, - starRingAut_apply, mul_comm] + simp [starComp_apply, mul_comm] @[simp] lemma star_apply [StarRing R'] (χ : MulChar R R') (a : R) : (star χ) a = star (χ a) := diff --git a/Mathlib/NumberTheory/NumberField/Basic.lean b/Mathlib/NumberTheory/NumberField/Basic.lean index c9e3ddec84ca4b..250a8d6db21c0b 100644 --- a/Mathlib/NumberTheory/NumberField/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/Basic.lean @@ -13,6 +13,7 @@ public import Mathlib.RingTheory.DedekindDomain.IntegralClosure /-! # Number fields + This file defines a number field and the ring of integers corresponding to it. ## Main definitions diff --git a/Mathlib/NumberTheory/NumberField/CMField.lean b/Mathlib/NumberTheory/NumberField/CMField.lean index 28a239a583205c..2e648b6a6623ec 100644 --- a/Mathlib/NumberTheory/NumberField/CMField.lean +++ b/Mathlib/NumberTheory/NumberField/CMField.lean @@ -349,7 +349,7 @@ theorem index_unitsMulComplexConjInv_range_dvd : refine this ▸ Subgroup.index_dvd_of_le ?_ rintro _ ⟨ζ, _, rfl⟩ exact ⟨ζ, Subtype.ext_iff.mpr (by simp [pow_two])⟩ - rw [IsCyclic.index_powMonoidHom_range, Nat.gcd_eq_right_iff_dvd, Nat.card_eq_fintype_card] + rw [IsCyclic.index_powMonoidHom_range, Nat.gcd_eq_right_iff_dvd] exact Even.two_dvd <| even_torsionOrder K /-- @@ -364,7 +364,6 @@ theorem indexRealUnits_mul_eq : convert! (Subgroup.index_map (torsion K) (unitsMulComplexConjInv K)).symm · rw [unitsMulComplexConjInv_ker] · rw [map_unitsMulComplexConjInv_torsion, IsCyclic.index_powMonoidHom_range, Nat.gcd_eq_right] - rw [Nat.card_eq_fintype_card] exact even_iff_two_dvd.mp (even_torsionOrder K) /-- diff --git a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean index b2a46262a4ce6e..928a5fbe2c5dfc 100644 --- a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/Basic.lean @@ -231,14 +231,15 @@ instance : IsAddHaarMeasure (volume : Measure (mixedSpace K)) := prod.instIsAddHaarMeasure volume volume open Classical in -instance : NoAtoms (volume : Measure (mixedSpace K)) := by +instance : NullSingletonClass (volume : Measure (mixedSpace K)) := by obtain ⟨w⟩ := (inferInstance : Nonempty (InfinitePlace K)) by_cases hw : IsReal w - · have : NoAtoms (volume : Measure ({w : InfinitePlace K // IsReal w} → ℝ)) := pi_noAtoms ⟨w, hw⟩ - exact prod.instNoAtoms_fst - · have : NoAtoms (volume : Measure ({w : InfinitePlace K // IsComplex w} → ℂ)) := - pi_noAtoms ⟨w, not_isReal_iff_isComplex.mp hw⟩ - exact prod.instNoAtoms_snd + · have : NullSingletonClass (volume : Measure ({w : InfinitePlace K // IsReal w} → ℝ)) := + pi_nullSingletonClass ⟨w, hw⟩ + exact prod.instNullSingletonClass_fst + · have : NullSingletonClass (volume : Measure ({w : InfinitePlace K // IsComplex w} → ℂ)) := + pi_nullSingletonClass ⟨w, not_isReal_iff_isComplex.mp hw⟩ + exact prod.instNullSingletonClass_snd variable {K} in open Classical in diff --git a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/FundamentalCone.lean b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/FundamentalCone.lean index 61cbe0591ffcd4..0d867ec9dcca5a 100644 --- a/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/FundamentalCone.lean +++ b/Mathlib/NumberTheory/NumberField/CanonicalEmbedding/FundamentalCone.lean @@ -470,7 +470,7 @@ theorem card_isPrincipal_norm_eq_mul_torsion (n : ℕ) : Nat.card {I : (Ideal (𝓞 K))⁰ | IsPrincipal (I : Ideal (𝓞 K)) ∧ absNorm (I : Ideal (𝓞 K)) = n} * torsionOrder K = Nat.card {a : integerSet K | mixedEmbedding.norm (a : mixedSpace K) = n} := by - rw [torsionOrder, ← Nat.card_eq_fintype_card, ← Nat.card_prod] + rw [torsionOrder, ← Nat.card_prod] exact Nat.card_congr (integerSetEquivNorm K n).symm variable (J : (Ideal (𝓞 K))⁰) @@ -578,7 +578,7 @@ theorem card_isPrincipal_dvd_norm_le (s : ℝ) : Nat.card {a : idealSet K J // mixedEmbedding.norm (a : mixedSpace K) ≤ s} := by obtain hs | hs := le_or_gt 0 s · simp_rw [← intNorm_idealSetEquiv_apply, ← Nat.le_floor_iff hs] - rw [torsionOrder, ← Nat.card_eq_fintype_card, ← Nat.card_prod] + rw [torsionOrder, ← Nat.card_prod] refine Nat.card_congr <| @Equiv.ofFiberEquiv _ (γ := Finset.Iic ⌊s⌋₊) _ (fun I ↦ ⟨absNorm I.1.val.1, Finset.mem_Iic.mpr I.1.prop.2.2⟩) (fun a ↦ ⟨intNorm (idealSetEquiv K J a.1).1, Finset.mem_Iic.mpr a.prop⟩) fun ⟨i, hi⟩ ↦ ?_ diff --git a/Mathlib/NumberTheory/NumberField/ClassNumber.lean b/Mathlib/NumberTheory/NumberField/ClassNumber.lean index 488c642d93a9bf..1f63affdc3fca9 100644 --- a/Mathlib/NumberTheory/NumberField/ClassNumber.lean +++ b/Mathlib/NumberTheory/NumberField/ClassNumber.lean @@ -161,8 +161,7 @@ theorem isPrincipalIdealRing_of_isPrincipal_of_pow_le_of_mem_primesOver_of_mem_I refine le_floor ?_ have : P.IsMaximal := hP.isMaximal (by simpa using HP.2) have : (span {p}).IsMaximal := (hpprime (.under ℤ P)).isMaximal_span_singleton - simpa only [hspan, ← cast_pow, absNorm_eq_pow_inertiaDeg P (hpprime (hP.under _)), - inertiaDeg_eq_inertiaDeg'] using hPN + simpa only [hspan, ← cast_pow, ← natAbs_pow_inertiaDeg' p P] using hPN have hpabsprime := Int.prime_iff_natAbs_prime.mp (hpprime (hP.under _)) refine h _ ?_ hpabsprime _ ⟨hP, ?_⟩ hple · suffices 0 < P.inertiaDeg' ℤ by diff --git a/Mathlib/NumberTheory/NumberField/Completion/FinitePlace.lean b/Mathlib/NumberTheory/NumberField/Completion/FinitePlace.lean index ff7e2f7603593e..4241695e4ee78e 100644 --- a/Mathlib/NumberTheory/NumberField/Completion/FinitePlace.lean +++ b/Mathlib/NumberTheory/NumberField/Completion/FinitePlace.lean @@ -18,6 +18,7 @@ import Mathlib.Algebra.FiniteSupport.Basic /-! # Finite places of number fields + This file defines finite places of a number field `K` as absolute values coming from an embedding into a completion of `K` associated to a non-zero prime ideal of `𝓞 K`. @@ -175,58 +176,71 @@ theorem adicAbv_natCast_le_one (n : ℕ) : adicAbv K v n ≤ 1 := theorem adicAbv_intCast_le_one (n : ℤ) : adicAbv K v n ≤ 1 := (isNonarchimedean_adicAbv K v).apply_intCast_le_one +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.one_lt_absNorm := one_lt_absNorm @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.one_lt_absNorm := one_lt_absNorm +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.one_lt_absNorm_nnreal := one_lt_absNorm_nnreal @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.one_lt_absNorm_nnreal := one_lt_absNorm_nnreal +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.absNorm_ne_zero := absNorm_ne_zero @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.absNorm_ne_zero := absNorm_ne_zero +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv := adicAbv @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv := adicAbv +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv_def := adicAbv_def @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv_def := adicAbv_def +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.isNonarchimedean_adicAbv := isNonarchimedean_adicAbv @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.isNonarchimedean_adicAbv := isNonarchimedean_adicAbv +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.instRankOneAdicCompletion := instRankOneAdicCompletion @[deprecated (since := "2026-03-11")] alias _root_.NumberField.instRankOneAdicCompletion := instRankOneAdicCompletion +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.instNormedFieldValuedAdicCompletion := instNormedFieldValuedAdicCompletion @[deprecated (since := "2026-03-11")] alias _root_.NumberField.instNormedFieldValuedAdicCompletion := instNormedFieldValuedAdicCompletion +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.rankOne_hom'_def := rankOne_hom'_def @[deprecated (since := "2026-03-11")] alias _root_.NumberField.rankOne_hom'_def := rankOne_hom'_def +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.toNNReal_valued_eq_adicAbv := toNNReal_valued_eq_adicAbv @[deprecated (since := "2026-03-11")] alias _root_.NumberField.toNNReal_valued_eq_adicAbv := toNNReal_valued_eq_adicAbv +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv_add_le_max := adicAbv_add_le_max @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv_add_le_max := adicAbv_add_le_max +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv_natCast_le_one := adicAbv_natCast_le_one @[deprecated (since := "2026-03-11")] alias _root_.NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv_natCast_le_one := adicAbv_natCast_le_one +set_option linter.dupNamespace false in @[deprecated (since := "2026-03-11")] alias NumberField.RingOfIntegers.HeightOneSpectrum.adicAbv_intCast_le_one := adicAbv_intCast_le_one @[deprecated (since := "2026-03-11")] diff --git a/Mathlib/NumberTheory/NumberField/Completion/Ramification.lean b/Mathlib/NumberTheory/NumberField/Completion/Ramification.lean index d38f345250f80c..1e00c9583a5808 100644 --- a/Mathlib/NumberTheory/NumberField/Completion/Ramification.lean +++ b/Mathlib/NumberTheory/NumberField/Completion/Ramification.lean @@ -6,7 +6,7 @@ Authors: Salvatore Mercuri module public import Mathlib.NumberTheory.NumberField.Completion.LiesOverInstances -public import Mathlib.NumberTheory.RamificationInertia.Basic +public import Mathlib.RingTheory.RamificationInertia.Inertia /-! # Ramification theory of completions of number fields @@ -86,16 +86,15 @@ variable (w) open scoped Classical in /-- The inertia degree of `w` over `v`. -/ protected noncomputable def inertiaDeg : ℕ := - if _ : w.1.LiesOver v.1 then (⊥ : Ideal v.Completion).inertiaDeg (⊥ : Ideal w.Completion) else 0 + if _ : w.1.LiesOver v.1 then (⊥ : Ideal w.Completion).inertiaDeg' v.Completion else 0 theorem inertiaDeg_of_liesOver [w.1.LiesOver v.1] : - v.inertiaDeg w = (⊥ : Ideal v.Completion).inertiaDeg (⊥ : Ideal w.Completion) := by + v.inertiaDeg w = (⊥ : Ideal w.Completion).inertiaDeg' v.Completion := by simp only [InfinitePlace.inertiaDeg, dif_pos] theorem inertiaDeg_eq_finrank [w.1.LiesOver v.1] : v.inertiaDeg w = Module.finrank v.Completion w.Completion := by - simp only [inertiaDeg_of_liesOver, Ideal.inertiaDeg, Ideal.comap_bot_of_injective _ <| - FaithfulSMul.algebraMap_injective v.Completion w.Completion] + rw [inertiaDeg_of_liesOver, Ideal.inertiaDeg'_eq_of_isMaximal ⊥] exact Algebra.finrank_eq_of_equiv_equiv (RingEquiv.quotientBot v.Completion) (RingEquiv.quotientBot w.Completion) (by ext; simp [RingHom.algebraMap_toAlgebra]) diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean index 35c65e0e82bdfa..e177981d40d19d 100644 --- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Basic.lean @@ -5,6 +5,7 @@ Authors: Riccardo Brasca -/ module +public import Mathlib.LinearAlgebra.FreeModule.IdealQuotient public import Mathlib.NumberTheory.Cyclotomic.Discriminant public import Mathlib.NumberTheory.NumberField.Cyclotomic.Embeddings public import Mathlib.NumberTheory.NumberField.Discriminant.Different @@ -13,6 +14,7 @@ public import Mathlib.RingTheory.Prime /-! # Ring of integers of cyclotomic fields + We gather results about cyclotomic extensions of `ℚ`. In particular, we compute the ring of integers of a cyclotomic extension of `ℚ`. @@ -187,6 +189,10 @@ end CharZero lemma coe_toInteger {k : ℕ} [NeZero k] (hζ : IsPrimitiveRoot ζ k) : hζ.toInteger.1 = ζ := rfl +@[simp] +lemma toInteger_coe {k : ℕ} [NeZero k] {x : 𝓞 K} (hx : IsPrimitiveRoot (x : K) k) : + hx.toInteger = x := rfl + /-- `𝓞 K ⧸ Ideal.span {ζ - 1}` is finite. -/ lemma finite_quotient_toInteger_sub_one [NumberField K] {k : ℕ} (hk : 1 < k) (hζ : IsPrimitiveRoot ζ k) : @@ -220,9 +226,7 @@ theorem integralPowerBasisOfPrimePow_gen [hcycl : IsCyclotomicExtension {p ^ k} simp only [adjoinEquivRingOfIntegersOfPrimePow_apply, IsIntegralClosure.algebraMap_lift] rfl -set_option linter.unusedVariables false in -/- We name `hcycl` so it can be used as a named argument, but this is unused in the declaration -otherwise, so we need to disable the linter. -/ +/- We name `hcycl` so it can be used as a named argument. -/ @[simp] theorem integralPowerBasisOfPrimePow_dim [hcycl : IsCyclotomicExtension {p ^ k} ℚ K] (hζ : IsPrimitiveRoot ζ (p ^ k)) : hζ.integralPowerBasisOfPrimePow.dim = φ (p ^ k) := by @@ -899,9 +903,8 @@ section NumberField open Units -theorem NumberField.Units.dvd_torsionOrder_of_isPrimitiveRoot [NeZero n] [NumberField K] {ζ : K} +theorem NumberField.Units.dvd_torsionOrder_of_isPrimitiveRoot [NeZero n] {ζ : K} (hζ : IsPrimitiveRoot ζ n) : n ∣ torsionOrder K := by - rw [torsionOrder, Fintype.card_eq_nat_card] replace hζ := (hζ.toInteger_isPrimitiveRoot).isUnit_unit (NeZero.ne n) convert! orderOf_dvd_natCard (⟨(hζ.isUnit (NeZero.ne n)).unit, ?_⟩ : torsion K) · rw [Subgroup.orderOf_mk] @@ -920,7 +923,6 @@ theorem IsCyclotomicExtension.Rat.torsionOrder_eq [NeZero n] [NumberField K] have hζ := hK.zeta_spec -- We first prove that `K` contains a primitive root of order `torsionOrder K` obtain ⟨μ, hμ⟩ : ∃ μ : torsion K, orderOf μ = torsionOrder K := by - rw [torsionOrder, Fintype.card_eq_nat_card] exact IsCyclic.exists_ofOrder_eq_natCard rw [← IsPrimitiveRoot.iff_orderOf, ← IsPrimitiveRoot.coe_submonoidClass_iff, ← IsPrimitiveRoot.coe_units_iff] at hμ diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Embeddings.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Embeddings.lean index da237c16e2618c..ef93768b70ba99 100644 --- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Embeddings.lean +++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Embeddings.lean @@ -10,6 +10,7 @@ public import Mathlib.NumberTheory.NumberField.InfinitePlace.TotallyRealComplex /-! # Cyclotomic extensions of `ℚ` are totally complex number fields. + We prove that cyclotomic extensions of `ℚ` are totally complex, meaning that `NrRealPlaces K = 0` if `IsCyclotomicExtension {n} ℚ K` and `2 < n`. diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean index a83cce9bee0610..6e5aec0b04a07a 100644 --- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean +++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Galois.lean @@ -122,7 +122,7 @@ theorem mem_zpowers_galEquivZMod_of_mem_stabilizer {σ : Gal(K/ℚ)} (hσ : σ have h₀ : IsPrimitiveRoot (Ideal.Quotient.mk P hζ.toInteger) n := by refine hζ.toInteger_isPrimitiveRoot.idealQuotient_mk (by simpa using IsMaximal.ne_top inferInstance) ?_ - rw [Ideal.absNorm_eq_pow_inertiaDeg' _ hp.out] + rw [← pow_inertiaDeg' p] exact Nat.Coprime.pow_left _ hn have h₁ := IsFractionRing.stabilizerHom_apply_apply_mk Gal(K/ℚ) (Ideal.span {(p : ℤ)}) P (ℤ ⧸ span {(p : ℤ)}) (𝓞 K ⧸ P) ⟨σ, hσ⟩ hζ.toInteger diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean index e8061a821c8f55..1858926305d56b 100644 --- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean +++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Ideal.lean @@ -47,9 +47,9 @@ namespace IsCyclotomicExtension.Rat open Ideal NumberField RingOfIntegers variable (n m p k : ℕ) [hp : Fact (Nat.Prime p)] (K : Type*) [Field K] [NumberField K] - (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver (Ideal.span {(p : ℤ)})] + (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver (span {(p : ℤ)})] -local notation3 "𝒑" => (Ideal.span {(p : ℤ)}) +local notation3 "𝒑" => (span {(p : ℤ)}) section PrimePow @@ -57,7 +57,7 @@ variable {K} [hK : IsCyclotomicExtension {p ^ (k + 1)} ℚ K] {ζ : K} (hζ : IsPrimitiveRoot ζ (p ^ (k + 1))) instance isPrime_span_zeta_sub_one : IsPrime (span {hζ.toInteger - 1}) := by - rw [Ideal.span_singleton_prime] + rw [span_singleton_prime] · exact hζ.zeta_sub_one_prime · exact Prime.ne_zero hζ.zeta_sub_one_prime @@ -72,12 +72,20 @@ theorem associated_norm_zeta_sub_one : Associated (Algebra.norm ℤ (hζ.toInteg rw [hζ.norm_toInteger_sub_one_of_eq_two_pow, h, Int.ofNat_two] · rw [hζ.norm_toInteger_sub_one_of_prime_ne_two h] +/-- An integer `n` is divisible by `ζ - 1` in `𝓞 K` if and only if it is divisible by `p`, +where `ζ` is a primitive `p ^ (k + 1)`-th root of unity. -/ +theorem zeta_sub_one_dvd_intCast_iff {n : ℤ} : + hζ.toInteger - 1 ∣ (n : 𝓞 K) ↔ (p : ℤ) ∣ n := by + have h := associated_norm_zeta_sub_one p k hζ + rw [← Ideal.norm_dvd_iff (h.symm.prime (Nat.prime_iff_prime_int.mp hp.out))] + exact h.dvd_iff_dvd_left + theorem absNorm_span_zeta_sub_one : absNorm (span {hζ.toInteger - 1}) = p := by simpa using congr_arg absNorm <| span_singleton_eq_span_singleton.mpr <| associated_norm_zeta_sub_one p k hζ theorem p_mem_span_zeta_sub_one : (p : 𝓞 K) ∈ span {hζ.toInteger - 1} := by - convert! Ideal.absNorm_mem _ + convert! absNorm_mem _ exact (absNorm_span_zeta_sub_one ..).symm theorem span_zeta_sub_one_ne_bot : span {hζ.toInteger - 1} ≠ ⊥ := @@ -85,14 +93,13 @@ theorem span_zeta_sub_one_ne_bot : span {hζ.toInteger - 1} ≠ ⊥ := instance liesOver_span_zeta_sub_one : (span {hζ.toInteger - 1}).LiesOver 𝒑 := by rw [liesOver_iff] - refine Ideal.IsMaximal.eq_of_le (Int.ideal_span_isMaximal_of_prime p) IsPrime.ne_top' ?_ + refine IsMaximal.eq_of_le (Int.ideal_span_isMaximal_of_prime p) IsPrime.ne_top' ?_ rw [span_singleton_le_iff_mem, mem_comap, algebraMap_int_eq, map_natCast] exact p_mem_span_zeta_sub_one p k hζ theorem inertiaDeg_span_zeta_sub_one : inertiaDeg' (span {hζ.toInteger - 1}) ℤ = 1 := by have : IsMaximal (span {hζ.toInteger - 1}) := .of_liesOver_isMaximal _ 𝒑 - rw [← inertiaDeg_eq_inertiaDeg' 𝒑] - rw [← Nat.pow_right_inj hp.out.one_lt, pow_one, ← absNorm_eq_pow_inertiaDeg' _ hp.out, + rw [← Nat.pow_right_inj hp.out.one_lt, pow_one, pow_inertiaDeg', absNorm_span_zeta_sub_one] attribute [local instance] FractionRing.liftAlgebra in @@ -115,12 +122,11 @@ theorem map_eq_span_zeta_sub_one_pow : ← Nat.card_eq_fintype_card, IsGalois.card_aut_eq_finrank] theorem ramificationIdx_span_zeta_sub_one : - ramificationIdx' (span {hζ.toInteger - 1}) ℤ = p ^ k * (p - 1) := by + ramificationIdx (span {hζ.toInteger - 1}) ℤ = p ^ k * (p - 1) := by have h := isPrime_span_zeta_sub_one p k hζ have hp0 : 𝒑 ≠ ⊥ := by simpa using hp.out.ne_zero - rw [← ramificationIdx_eq_ramificationIdx' 𝒑 _ hp0, - ← Nat.totient_prime_pow_succ hp.out, ← finrank _ K, - IsDedekindDomain.ramificationIdx_eq_multiplicity _ h, map_eq_span_zeta_sub_one_pow p k hζ, + rw [← Nat.totient_prime_pow_succ hp.out, ← finrank _ K, + IsDedekindDomain.ramificationIdx_eq_multiplicity 𝒑, map_eq_span_zeta_sub_one_pow p k hζ, multiplicity_pow_self (span_zeta_sub_one_ne_bot p k hζ) (isUnit_iff.not.mpr h.ne_top)] exact map_ne_bot_of_ne_bot hp0 @@ -155,7 +161,7 @@ theorem inertiaDeg_eq_of_prime_pow (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP include hK in theorem ramificationIdx_eq_of_prime_pow (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver 𝒑] : - ramificationIdx' P ℤ = p ^ k * (p - 1) := by + ramificationIdx P ℤ = p ^ k * (p - 1) := by rw [eq_span_zeta_sub_one_of_liesOver p k K hK.zeta_spec P, ramificationIdx_span_zeta_sub_one] include hK in @@ -182,15 +188,62 @@ instance isPrime_span_zeta_sub_one' : IsPrime (span {hζ.toInteger - 1}) := by rw [← pow_one p] at hK hζ exact isPrime_span_zeta_sub_one p 0 hζ +/-- If `2 < p`, then `2` is not in the ideal `(ζ - 1)`, where `ζ` is a primitive `p`-th root of +unity. -/ +theorem two_not_mem_span_zeta_sub_one' (h : 2 < p) : (2 : 𝓞 K) ∉ span {hζ.toInteger - 1} := by + rw [mem_span_singleton] + rw [← pow_one p] at hK hζ + exact hζ.toInteger_sub_one_not_dvd_two h.ne' + +omit hp hK [NumberField K] in +lemma associated_sub_one_of_isPrimitiveRoot [NeZero p] {η : K} (hη : IsPrimitiveRoot η p) : + Associated (hζ.toInteger - 1) (hη.toInteger - 1) := by + obtain ⟨i, -, hi, hζη⟩ := hζ.isPrimitiveRoot_iff.mp hη + rw [show hη.toInteger = hζ.toInteger ^ i from RingOfIntegers.ext hζη.symm] + exact hζ.toInteger_isPrimitiveRoot.associated_sub_one_pow_sub_one_of_coprime hi + +omit [NumberField K] hK in +open Polynomial in +/-- `(ζ - 1) ^ (p - 1)` is associated to `p`, where `ζ` is a primitive `p`-th root of unity and +`p` is prime. -/ +theorem associated_zeta_sub_one_pow_prime : + Associated ((hζ.toInteger - 1) ^ (p - 1)) (p : 𝓞 K) := by + rw [← eval_one_cyclotomic_prime (R := 𝓞 K) (p := p), + cyclotomic_eq_prod_X_sub_primitiveRoots hζ.toInteger_isPrimitiveRoot, eval_prod] + simp only [eval_sub, eval_X, eval_C] + rw [← Nat.totient_prime hp.out, ← hζ.toInteger_isPrimitiveRoot.card_primitiveRoots, + ← Finset.prod_const] + refine Associated.prod _ _ _ fun η hη ↦ ?_ + have hη' : IsPrimitiveRoot (η : K) p := + (isPrimitiveRoot_of_mem_primitiveRoots hη).map_of_injective RingOfIntegers.coe_injective + simpa using (associated_sub_one_of_isPrimitiveRoot p hζ hη').neg_right + +/-- If `ζ - 1` does not divide `x`, then `p` and `x` are coprime, where `ζ` is a primitive `p`-th +root of unity and `p` is prime. -/ +theorem isCoprime_of_not_zeta_sub_one_dvd {x : 𝓞 K} (hx : ¬ hζ.toInteger - 1 ∣ x) : + IsCoprime (p : 𝓞 K) x := by + rwa [← isCoprime_span_singleton_iff, ← span_singleton_eq_span_singleton.mpr + (associated_zeta_sub_one_pow_prime p hζ), ← span_singleton_pow, + IsCoprime.pow_left_iff (by grind [hp.out.one_lt]), isCoprime_iff_gcd, + (prime_span_singleton_iff.mpr + hζ.zeta_sub_one_prime').irreducible.gcd_eq_one_iff, dvd_span_singleton, mem_span_singleton] + theorem inertiaDeg_span_zeta_sub_one' : inertiaDeg' (span {hζ.toInteger - 1}) ℤ = 1 := by rw [← pow_one p] at hK hζ exact inertiaDeg_span_zeta_sub_one p 0 hζ theorem ramificationIdx_span_zeta_sub_one' : - ramificationIdx' (span {hζ.toInteger - 1}) ℤ = p - 1 := by + ramificationIdx (span {hζ.toInteger - 1}) ℤ = p - 1 := by rw [← pow_one p] at hK hζ rw [ramificationIdx_span_zeta_sub_one p 0 hζ, pow_zero, one_mul] +/-- An integer `n` is divisible by `ζ - 1` in `𝓞 K` if and only if it is divisible by `p`, +where `ζ` is a primitive `p`-th root of unity. -/ +theorem zeta_sub_one_dvd_intCast_iff' {n : ℤ} : + hζ.toInteger - 1 ∣ (n : 𝓞 K) ↔ (p : ℤ) ∣ n := by + rw [← pow_one p] at hK hζ + exact zeta_sub_one_dvd_intCast_iff p 0 hζ + variable (K) include hK in @@ -211,7 +264,7 @@ theorem inertiaDeg_eq_of_prime (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : include hK in theorem ramificationIdx_eq_of_prime (P : Ideal (𝓞 K)) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver 𝒑] : - ramificationIdx' P ℤ = p - 1 := by + ramificationIdx P ℤ = p - 1 := by rw [eq_span_zeta_sub_one_of_liesOver' p K hK.zeta_spec P, ramificationIdx_span_zeta_sub_one'] include hK in @@ -250,7 +303,6 @@ theorem inertiaDeg_eq_of_not_dvd (hm : ¬ p ∣ m) : rw [Multiset.mem_toFinset, Polynomial.mem_normalizedFactors_iff (map_monic_ne_zero (minpoly.monic ζ.isIntegral))] at h₂ have : P.IsMaximal := .of_liesOver_isMaximal P 𝒑 - rw [← inertiaDeg_eq_inertiaDeg' 𝒑] rw [h₃, natDegree_of_dvd_cyclotomic_of_irreducible (by simp) hm (f := 1) _ h₂.1] · simpa using (orderOf_injective _ Units.coeHom_injective (ZMod.unitOfCoprime p hm)).symm · refine dvd_trans h₂.2.2 ?_ @@ -262,7 +314,7 @@ theorem inertiaDeg_eq_of_not_dvd (hm : ¬ p ∣ m) : alias inertiaDeg_of_not_dvd := inertiaDeg_eq_of_not_dvd theorem ramificationIdx_eq_of_not_dvd (hm : ¬ p ∣ m) : - ramificationIdx' P ℤ = 1 := by + ramificationIdx P ℤ = 1 := by let ζ := (zeta_spec m ℚ K).toInteger have h₁ : ¬ p ∣ exponent ζ := by rw [exponent_eq_one_iff.mpr <| adjoin_singleton_eq_top (zeta_spec m ℚ K)] @@ -272,7 +324,7 @@ theorem ramificationIdx_eq_of_not_dvd (hm : ¬ p ∣ m) : simp only [Subtype.coe_eta, Equiv.symm_apply_apply] at h₃ rw [Multiset.mem_toFinset, Polynomial.mem_normalizedFactors_iff (map_monic_ne_zero (minpoly.monic ζ.isIntegral))] at h₂ - rw [← ramificationIdx_eq_ramificationIdx' 𝒑 P (by simpa using hp.out.ne_zero), h₃] + rw [h₃] refine multiplicity_eq_of_emultiplicity_eq_some (le_antisymm ?_ ?_) · apply emultiplicity_le_one_of_separable · exact isUnit_iff_degree_eq_zero.not.mpr (Irreducible.degree_pos h₂.1).ne' @@ -347,7 +399,7 @@ private theorem inertiaDegIn_ramificationIdxIn_aux (hn : n = p ^ (k + 1) * m) (h ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝒑 (𝓞 Fₘ) Gal(Fₘ/ℚ), ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝒑 (𝓞 Fₚ) Gal(Fₚ/ℚ), ← ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝒑 (𝓞 K) Gal(K/ℚ), - ← ncard_primesOver_mul_ncard_primesOver Pₘ Gal(Fₘ/ℚ) (𝓞 K) Gal(K/ℚ) Gal(K/Fₘ), + ← ncard_primesOver_mul_ncard_primesOver Pₘ Gal(Fₘ/ℚ) (𝓞 K) Gal(K/ℚ), ramificationIdxIn_eq_of_not_dvd p Fₘ hm, inertiaDegIn_eq_of_prime_pow p k Fₚ, ncard_primesOver_of_prime_pow p k Fₚ, one_mul, one_mul, mul_one, mul_assoc, mul_assoc, mul_right_inj' (IsDedekindDomain.primesOver_ncard_ne_zero 𝒑 _), ← mul_assoc, @@ -380,7 +432,7 @@ theorem inertiaDeg_eq (hn : n = p ^ (k + 1) * m) (hm : ¬ p ∣ m) : rw [← inertiaDegIn_eq_inertiaDeg 𝒑 P Gal(K/ℚ), inertiaDegIn_eq n K hn hm] theorem ramificationIdx_eq (hn : n = p ^ (k + 1) * m) (hm : ¬ p ∣ m) : - ramificationIdx' P ℤ = p ^ k * (p - 1) := by + ramificationIdx P ℤ = p ^ k * (p - 1) := by have : IsGalois ℚ K := isGalois {n} ℚ K rw [← ramificationIdxIn_eq_ramificationIdx 𝒑 P Gal(K/ℚ), ramificationIdxIn_eq n K hn hm] diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/PID.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/PID.lean index 78d7a5298fd7f1..69f3a4c6633b1f 100644 --- a/Mathlib/NumberTheory/NumberField/Cyclotomic/PID.lean +++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/PID.lean @@ -11,6 +11,7 @@ public import Mathlib.NumberTheory.NumberField.Cyclotomic.Embeddings /-! # Cyclotomic fields whose ring of integers is a PID. + We prove that `ℤ [ζₚ]` is a PID for specific values of `p`. The result holds for `p ≤ 19`, but the proof is more and more involved. diff --git a/Mathlib/NumberTheory/NumberField/Cyclotomic/Three.lean b/Mathlib/NumberTheory/NumberField/Cyclotomic/Three.lean index c352aef3079703..6f903bf65bab97 100644 --- a/Mathlib/NumberTheory/NumberField/Cyclotomic/Three.lean +++ b/Mathlib/NumberTheory/NumberField/Cyclotomic/Three.lean @@ -12,6 +12,7 @@ public import Mathlib.RingTheory.Fintype /-! # Third Cyclotomic Field + We gather various results about the third cyclotomic field. The following notations are used in this file: `K` is a number field such that `IsCyclotomicExtension {3} ℚ K`, `ζ` is any primitive `3`-rd root of unity in `K`, `η` is the element in the units of the ring of integers corresponding to `ζ` diff --git a/Mathlib/NumberTheory/NumberField/Discriminant/Basic.lean b/Mathlib/NumberTheory/NumberField/Discriminant/Basic.lean index d144b529ff71ee..b07cdf0a60b849 100644 --- a/Mathlib/NumberTheory/NumberField/Discriminant/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/Discriminant/Basic.lean @@ -15,6 +15,7 @@ public import Mathlib.Analysis.SpecialFunctions.Log.Base /-! # Number field discriminant + This file defines the discriminant of a number field. ## Main result diff --git a/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean b/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean index 202534b3bb135d..505f6e860e994c 100644 --- a/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean +++ b/Mathlib/NumberTheory/NumberField/Discriminant/Defs.lean @@ -10,6 +10,7 @@ public import Mathlib.RingTheory.Localization.NormTrace /-! # Number field discriminant + This file defines the discriminant of a number field. ## Main definitions diff --git a/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean b/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean index ea87dce51ac262..ba9f70b1c8c348 100644 --- a/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean +++ b/Mathlib/NumberTheory/NumberField/Discriminant/Different.lean @@ -87,7 +87,7 @@ lemma discr_mem_differentIdeal : ↑(discr K) ∈ differentIdeal ℤ 𝒪 := by attribute [local instance] FractionRing.liftAlgebra in theorem natAbs_discr_eq_absNorm_differentIdeal_mul_natAbs_discr_pow (L 𝒪' : Type*) [Field L] - [NumberField L] [CommRing 𝒪'] [Algebra 𝒪' L] [IsFractionRing 𝒪' L] [IsIntegralClosure 𝒪' ℤ L] + [NumberField L] [CommRing 𝒪'] [Algebra 𝒪' L] [IsFractionRing 𝒪' L] [IsDedekindDomain 𝒪'] [CharZero 𝒪'] [Algebra K L] [Algebra 𝒪 𝒪'] [Algebra 𝒪 L] [IsScalarTower 𝒪 K L] [IsScalarTower 𝒪 𝒪' L] [IsTorsionFree 𝒪 𝒪'] [Free ℤ 𝒪'] [Module.Finite ℤ 𝒪'] [Module.Finite 𝒪 𝒪'] : @@ -189,9 +189,23 @@ lemma not_dvd_discr_iff_forall_liesOver [IsIntegralClosure 𝒪 ℤ K] {p : ℤ} exact ⟨P, hP, ⟨h₁.symm⟩, h₂⟩ · rintro ⟨P, hP, hP', hP''⟩ have := Ideal.absNorm_dvd_absNorm_of_le (Ideal.dvd_iff_le.mp hP'') - rw [absNorm_differentIdeal K, Ideal.absNorm_eq_pow_inertiaDeg P hp, + rw [absNorm_differentIdeal K, ← Ideal.natAbs_pow_inertiaDeg' p, ← Int.natAbs_pow, Int.natAbs_dvd_natAbs] at this - exact (dvd_pow_self _ (Ideal.inertiaDeg_pos' ..).ne').trans this + exact (dvd_pow_self _ (Ideal.inertiaDeg'_pos ..).ne').trans this + +/-- A prime `p` does not divide `discr K` if and only if `p` (as the ideal `span {p}`) is +unramified in the ring of integers `𝒪`. + +Also see `not_dvd_discr_iff_forall_liesOver` and `not_dvd_discr_iff_forall_mem` for variants +whose RHS does not use `Algebra.IsUnramifiedIn`. -/ +lemma not_dvd_discr_iff_isUnramifiedIn [IsIntegralClosure 𝒪 ℤ K] {p : ℤ} (hp : Prime p) : + ¬ p ∣ discr K ↔ Algebra.IsUnramifiedIn 𝒪 (Ideal.span {p}) := by + have := (IsIntegralClosure.algebraMap_injective 𝒪 ℤ K).isDomain + have := IsIntegralClosure.isDedekindDomain ℤ ℚ K 𝒪 + have := CharZero.of_module (R := 𝒪) K + rw [not_dvd_discr_iff_forall_liesOver K 𝒪 hp] + exact (Algebra.isUnramifiedIn_iff_forall_of_isDedekindDomain' + (Ideal.span_singleton_eq_bot.not.mpr hp.ne_zero)).symm /-- Also see `not_dvd_discr_iff_forall_liesOver` for a slightly easier to prove RHS. -/ lemma not_dvd_discr_iff_forall_mem [IsIntegralClosure 𝒪 ℤ K] {p : ℤ} (hp : Prime p) : diff --git a/Mathlib/NumberTheory/NumberField/EquivReindex.lean b/Mathlib/NumberTheory/NumberField/EquivReindex.lean index c2ac127cc3b9b0..1eaf27e1199dea 100644 --- a/Mathlib/NumberTheory/NumberField/EquivReindex.lean +++ b/Mathlib/NumberTheory/NumberField/EquivReindex.lean @@ -8,8 +8,8 @@ module public import Mathlib.NumberTheory.NumberField.CanonicalEmbedding.Basic /-! - # Reindexed basis + This file introduces an equivalence between the set of embeddings of `K` into `ℂ` and the index set of the chosen basis of the ring of integers of `K`. diff --git a/Mathlib/NumberTheory/NumberField/ExistsRamified.lean b/Mathlib/NumberTheory/NumberField/ExistsRamified.lean index f1c8f469117601..39c74954022c07 100644 --- a/Mathlib/NumberTheory/NumberField/ExistsRamified.lean +++ b/Mathlib/NumberTheory/NumberField/ExistsRamified.lean @@ -13,6 +13,7 @@ public import Mathlib.RingTheory.Unramified.Dedekind /-! # Every number field has a ramified prime over `ℚ` + ...except `ℚ` itself. This is a trivial corollary of `NumberField.not_dvd_discr_iff_forall_mem` and @@ -26,20 +27,25 @@ open scoped NumberField nonZeroDivisors variable {K 𝒪 : Type*} [Field K] [NumberField K] [CommRing 𝒪] [Algebra 𝒪 K] variable [IsIntegralClosure 𝒪 ℤ K] +/-- If `K` is a number field with positive rank, then some prime is ramified in `K`. -/ +lemma NumberField.exists_not_isUnramifiedIn (H : Module.finrank ℚ K ≠ 1) : + ∃ p : ℕ, p.Prime ∧ ¬ Algebra.IsUnramifiedIn 𝒪 (Ideal.span {(p : ℤ)}) := by + have : 0 < Module.finrank ℚ K := Module.finrank_pos + have : 2 < |discr K| := abs_discr_gt_two (by lia) + obtain ⟨p, hp1, hp2⟩ := (discr K).exists_prime_and_dvd (by linarith) + use p.natAbs, p.prime_iff_natAbs_prime.mp hp1 + simpa [← not_dvd_discr_iff_isUnramifiedIn K 𝒪 hp1] + /-- If `K` is a number field with positive rank, then there exists some maximal ideal of `𝓞 K` that is ramified over `ℤ`. -/ lemma NumberField.exists_not_isUnramifiedAt_int (H : Module.finrank ℚ K ≠ 1) : ∃ (P : Ideal 𝒪) (_ : P.IsMaximal), ¬ Algebra.IsUnramifiedAt ℤ P := by + obtain ⟨p, hp1, hp2⟩ := NumberField.exists_not_isUnramifiedIn (𝒪 := 𝒪) H have := (IsIntegralClosure.algebraMap_injective 𝒪 ℤ K).isDomain have := IsIntegralClosure.isDedekindDomain ℤ ℚ K 𝒪 - have := CharZero.of_module (R := 𝒪) K - have := Module.finrank_pos (R := ℚ) (M := K) - have := NumberField.abs_discr_gt_two (K := K) (by lia) - obtain ⟨q, hq, hqK⟩ := Int.exists_prime_and_dvd (n := discr K) (by zify; linarith) - have := (not_dvd_discr_iff_forall_mem K 𝒪 hq).not_right.mp hqK - push Not at this - obtain ⟨P, hP, h, H⟩ := this - exact ⟨P, hP.isMaximal (by aesop), H⟩ + have := IsIntegralClosure.isTorsionFree ℤ (A := 𝒪) K + have := IsIntegralClosure.isIntegral_algebra ℤ (A := 𝒪) K + grind [Algebra.isUnramifiedIn_iff_forall_of_isDedekindDomain] /-- Any number field that is unramified over `ℚ` has rank `1`. -/ lemma NumberField.finrank_eq_one_of_unramified [Algebra.Unramified ℤ 𝒪] : @@ -84,6 +90,6 @@ lemma NumberField.exists_not_isUnramifiedAt_int_of_isGalois [IsGalois ℚ K] (map_dvd (algebraMap _ _) p.associated_natAbs.symm.dvd) (by simpa using hQ) have : .span {p} = Ideal.under ℤ Q := ((Ideal.liesOver_span_iff Ideal.IsPrime.ne_top' this).mpr hQ).1 - rwa [← Ideal.ramificationIdx'_eq_one_iff, + rwa [← Ideal.ramificationIdx_eq_one_iff, ← Ideal.ramificationIdxIn_eq_ramificationIdx (Q.under ℤ) _ Gal(K/ℚ), ← this, ← hp, - Ideal.ramificationIdxIn_eq_ramificationIdx _ P Gal(K/ℚ), Ideal.ramificationIdx'_eq_one_iff] + Ideal.ramificationIdxIn_eq_ramificationIdx _ P Gal(K/ℚ), Ideal.ramificationIdx_eq_one_iff] diff --git a/Mathlib/NumberTheory/NumberField/House.lean b/Mathlib/NumberTheory/NumberField/House.lean index 403ac21e762d0f..7c54a42afb9a35 100644 --- a/Mathlib/NumberTheory/NumberField/House.lean +++ b/Mathlib/NumberTheory/NumberField/House.lean @@ -10,8 +10,8 @@ public import Mathlib.NumberTheory.NumberField.CanonicalEmbedding.Basic public import Mathlib.NumberTheory.NumberField.EquivReindex /-! - # House of an algebraic number + This file defines the house of an algebraic number `α`, which is the largest of the modulus of its conjugates. diff --git a/Mathlib/NumberTheory/NumberField/Ideal/Basic.lean b/Mathlib/NumberTheory/NumberField/Ideal/Basic.lean index bfc25c1c16882b..885aaaba9c7c07 100644 --- a/Mathlib/NumberTheory/NumberField/Ideal/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/Ideal/Basic.lean @@ -114,7 +114,7 @@ theorem NumberField.torsionOrder_dvd_absNorm_sub_one {P : Ideal (𝓞 K)} (hP₀ let _ := Ideal.Quotient.field P have hP₃ : absNorm P ≠ 1 := absNorm_eq_one_iff.not.mpr <| IsPrime.ne_top hP₁ have h := Subgroup.card_dvd_of_injective _ (torsionMapQuot_injective hP₃ hP₂) - rwa [Nat.card_eq_fintype_card, Nat.card_units] at h + rwa [Nat.card_units] at h end torsionMapQuot diff --git a/Mathlib/NumberTheory/NumberField/Ideal/KummerDedekind.lean b/Mathlib/NumberTheory/NumberField/Ideal/KummerDedekind.lean index aa4c253c42aaef..d53e1be80c7114 100644 --- a/Mathlib/NumberTheory/NumberField/Ideal/KummerDedekind.lean +++ b/Mathlib/NumberTheory/NumberField/Ideal/KummerDedekind.lean @@ -7,7 +7,7 @@ module public import Mathlib.NumberTheory.KummerDedekind public import Mathlib.NumberTheory.NumberField.Basic -public import Mathlib.NumberTheory.RamificationInertia.Basic +public import Mathlib.RingTheory.RamificationInertia.Basic public import Mathlib.RingTheory.Ideal.Int /-! @@ -209,20 +209,24 @@ The residual degree of the ideal corresponding to the class of `Q ∈ ℤ[X]` mo -/ theorem inertiaDeg_primesOverSpanEquivMonicFactorsMod_symm_apply (hp : ¬ p ∣ exponent θ) {Q : ℤ[X]} (hQ : Q.map (Int.castRingHom (ZMod p)) ∈ monicFactorsMod θ p) : - inertiaDeg (span {(p : ℤ)}) ((primesOverSpanEquivMonicFactorsMod hp).symm - ⟨Q.map (Int.castRingHom (ZMod p)), hQ⟩ : Ideal (𝓞 K)) = + inertiaDeg' ((primesOverSpanEquivMonicFactorsMod hp).symm + ⟨Q.map (Int.castRingHom (ZMod p)), hQ⟩ : Ideal (𝓞 K)) ℤ = natDegree (Q.map (Int.castRingHom (ZMod p))) := by -- This is needed for `inertiaDeg_algebraMap` below to work + have : (span {↑p, (aeval θ) Q}).IsMaximal := by + rw [← Ideal.primesOverSpanEquivMonicFactorsMod_symm_apply_eq_span hp hQ] + apply Ideal.primesOver.isMaximal have := liesOver_primesOverSpanEquivMonicFactorsMod_symm hp hQ - rw [primesOverSpanEquivMonicFactorsMod_symm_apply_eq_span, inertiaDeg_algebraMap, + rw [primesOverSpanEquivMonicFactorsMod_symm_apply_eq_span, + inertiaDeg'_eq_of_isMaximal (span {(p : ℤ)}), ← finrank_quotient_span_eq_natDegree] refine Algebra.finrank_eq_of_equiv_equiv (Int.quotientSpanNatEquivZMod p) ?_ (by ext; simp) exact (ZModXQuotSpanEquivQuotSpanPair hp hQ).symm theorem inertiaDeg_primesOverSpanEquivMonicFactorsMod_symm_apply' (hp : ¬ p ∣ exponent θ) {Q : (ZMod p)[X]} (hQ : Q ∈ monicFactorsMod θ p) : - inertiaDeg (span {(p : ℤ)}) - ((primesOverSpanEquivMonicFactorsMod hp).symm ⟨Q, hQ⟩ : Ideal (𝓞 K)) = natDegree Q := by + inertiaDeg' + ((primesOverSpanEquivMonicFactorsMod hp).symm ⟨Q, hQ⟩ : Ideal (𝓞 K)) ℤ = natDegree Q := by obtain ⟨S, rfl⟩ := (map_surjective _ (ZMod.ringHom_surjective (Int.castRingHom (ZMod p)))) Q rw [inertiaDeg_primesOverSpanEquivMonicFactorsMod_symm_apply] @@ -233,12 +237,12 @@ The ramification index of the ideal corresponding to the class of `Q ∈ ℤ[X]` -/ theorem ramificationIdx_primesOverSpanEquivMonicFactorsMod_symm_apply (hp : ¬ p ∣ exponent θ) {Q : ℤ[X]} (hQ : Q.map (Int.castRingHom (ZMod p)) ∈ monicFactorsMod θ p) : - ramificationIdx (span {(p : ℤ)}) + ramificationIdx ((primesOverSpanEquivMonicFactorsMod hp).symm - ⟨Q.map (Int.castRingHom (ZMod p)), hQ⟩ : Ideal (𝓞 K)) = + ⟨Q.map (Int.castRingHom (ZMod p)), hQ⟩ : Ideal (𝓞 K)) ℤ = multiplicity (Q.map (Int.castRingHom (ZMod p))) ((minpoly ℤ θ).map (Int.castRingHom (ZMod p))) := by - rw [ramificationIdx_eq_multiplicity (map_ne_bot_of_ne_bot (by simp [NeZero.ne p])) inferInstance] + rw [ramificationIdx_eq_multiplicity (span {↑p}) _ (map_ne_bot_of_ne_bot (by simp [NeZero.ne p]))] · apply multiplicity_eq_of_emultiplicity_eq rw [← emultiplicity_map_eq (mapEquiv (Int.quotientSpanNatEquivZMod p).symm), emultiplicity_factors_map_eq_emultiplicity inferInstance (by simp [NeZero.ne p]) @@ -251,8 +255,8 @@ theorem ramificationIdx_primesOverSpanEquivMonicFactorsMod_symm_apply (hp : ¬ p theorem ramificationIdx_primesOverSpanEquivMonicFactorsMod_symm_apply' (hp : ¬ p ∣ exponent θ) {Q : (ZMod p)[X]} (hQ : Q ∈ monicFactorsMod θ p) : - ramificationIdx (span {(p : ℤ)}) - ((primesOverSpanEquivMonicFactorsMod hp).symm ⟨Q, hQ⟩ : Ideal (𝓞 K)) = + ramificationIdx + ((primesOverSpanEquivMonicFactorsMod hp).symm ⟨Q, hQ⟩ : Ideal (𝓞 K)) ℤ = multiplicity Q ((minpoly ℤ θ).map (Int.castRingHom (ZMod p))) := by obtain ⟨S, rfl⟩ := (map_surjective _ (ZMod.ringHom_surjective (Int.castRingHom (ZMod p)))) Q rw [ramificationIdx_primesOverSpanEquivMonicFactorsMod_symm_apply] diff --git a/Mathlib/NumberTheory/NumberField/InfinitePlace/Basic.lean b/Mathlib/NumberTheory/NumberField/InfinitePlace/Basic.lean index d5ffa594968644..5baa65ba634bb0 100644 --- a/Mathlib/NumberTheory/NumberField/InfinitePlace/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/InfinitePlace/Basic.lean @@ -309,6 +309,7 @@ open scoped Classical in protected noncomputable instance fintype [NumberField K] : Fintype (InfinitePlace K) := Set.fintypeRange _ +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias NumberField.InfinitePlace.fintype := InfinitePlace.fintype @@ -595,7 +596,6 @@ theorem isNontrivial : v.1.IsNontrivial := by variable {v} (K) -open Filter in /-- *Weak approximation for infinite places* The number field `K` is dense when embedded diagonally in the product @@ -603,39 +603,8 @@ The number field `K` is dense when embedded diagonally in the product topology coming from the infinite place `v`. -/ theorem denseRange_algebraMap_pi [NumberField K] : - DenseRange <| algebraMap K ((v : InfinitePlace K) → WithAbs v.1) := by - classical - -- We have to show that given `(zᵥ)ᵥ` with `zᵥ : WithAbs v.1`, there is a `y : K` that is - -- arbitrarily close to each `zᵥ` in `v`'s topology. - refine Metric.denseRange_iff.mpr fun z r hr ↦ ?_ - -- Given `v`, by previous results we can select a `aᵥ : K` for each infinite place `v` - -- such that `1 < v aᵥ` while `w aᵥ < 1` for all `w ≠ v`. - choose a hx using AbsoluteValue.exists_one_lt_lt_one_pi_of_not_isEquiv isNontrivial - fun _ _ hwv ↦ (eq_iff_isEquiv (K := K)).not.mp hwv - -- Define the sequence `yₙ = ∑ v, 1 / (1 + aᵥ⁻ⁿ) * zᵥ` in `K` - let y := fun n ↦ ∑ v, (1 / (1 + (a v)⁻¹ ^ n)) * WithAbs.equiv v.1 (z v) - -- We will show that this sequence converges to `z` in the product topology. - have : atTop.Tendsto - (fun n (v : InfinitePlace K) ↦ (WithAbs.equiv v.1).symm (y n)) (𝓝 z) := by - -- At a fixed place `u`, the limit of `y` with respect to `u`'s topology is `zᵤ`. - refine tendsto_pi_nhds.mpr fun u ↦ ?_ - simp_rw [← Fintype.sum_pi_single u z, y, map_sum, map_mul] - refine tendsto_finsetSum _ fun w _ ↦ ?_ - by_cases hw : u = w - · -- Because `1 / (1 + aᵤ⁻ⁿ) → 1` in `WithAbs u.1`. - rw [← hw, Pi.single_eq_same] - have : u (a u)⁻¹ < 1 := by simpa [← inv_pow, inv_lt_one_iff₀] using .inr (hx u).1 - simpa using (WithAbs.tendsto_one_div_one_add_pow_nhds_one this).mul_const (z u) - · -- And `1 / (1 + aᵤ⁻ⁿ) → 0` in `WithAbs w.1` when `w ≠ u`. - rw [Pi.single_eq_of_ne (M := fun v ↦ WithAbs v.1) hw (z w)] - have hu : 1 < u (a w)⁻¹ := by simpa [one_lt_inv_iff₀] using - ⟨u.pos_iff.2 fun ha ↦ by linarith [map_zero w ▸ ha ▸ (hx w).1], (hx w).2 u hw⟩ - have := u.1.tendsto_div_one_add_pow_nhds_zero hu - simp_rw [← WithAbs.norm_toAbs_eq] at this - simpa using (tendsto_zero_iff_norm_tendsto_zero.2 this).mul_const - ((WithAbs.equiv u.1).symm (WithAbs.equiv w.1 (z w))) - -- So taking a sufficiently large index of the sequence `yₙ` gives the desired term. - let ⟨N, h⟩ := Metric.tendsto_atTop.1 this r hr - exact ⟨y N, dist_comm z (algebraMap K _ (y N)) ▸ h N le_rfl⟩ + DenseRange <| algebraMap K ((v : InfinitePlace K) → WithAbs v.1) := + AbsoluteValue.denseRange_algebraMap_pi (fun v ↦ v.isNontrivial) + fun _ _ h ↦ (eq_iff_isEquiv (K := K)).not.mp h end NumberField.InfinitePlace diff --git a/Mathlib/NumberTheory/NumberField/Norm.lean b/Mathlib/NumberTheory/NumberField/Norm.lean index 5b6a741f521008..5df13dd08fc944 100644 --- a/Mathlib/NumberTheory/NumberField/Norm.lean +++ b/Mathlib/NumberTheory/NumberField/Norm.lean @@ -11,6 +11,7 @@ public import Mathlib.RingTheory.Norm.Transitivity /-! # Norm in number fields + Given a finite extension of number fields, we define the norm morphism as a function between the rings of integers. diff --git a/Mathlib/NumberTheory/NumberField/Units/Basic.lean b/Mathlib/NumberTheory/NumberField/Units/Basic.lean index 6a5ddcb0ad9a36..29c9a43acfd3ea 100644 --- a/Mathlib/NumberTheory/NumberField/Units/Basic.lean +++ b/Mathlib/NumberTheory/NumberField/Units/Basic.lean @@ -77,6 +77,11 @@ variable {K} theorem coe_coe (u : (𝓞 K)ˣ) : ((u : 𝓞 K) : K) = (u : K) := rfl +theorem _root_.IsPrimitiveRoot.coe_coe_iff {ν : (𝓞 K)ˣ} {n : ℕ} : + IsPrimitiveRoot (ν : K) n ↔ IsPrimitiveRoot ν n := + IsPrimitiveRoot.map_iff_of_injective + (f := (algebraMap (𝓞 K) K).toMonoidHom.comp (Units.coeHom (𝓞 K))) (coe_injective K) + theorem coe_mul (x y : (𝓞 K)ˣ) : ((x * y : (𝓞 K)ˣ) : K) = (x : K) * (y : K) := rfl theorem coe_pow (x : (𝓞 K)ˣ) (n : ℕ) : ((x ^ n : (𝓞 K)ˣ) : K) = (x : K) ^ n := by @@ -154,8 +159,7 @@ theorem mem_torsion {x : (𝓞 K)ˣ} : NumberField.RingOfIntegers.coe_eq_algebraMap, coe_one]⟩ /-- The torsion subgroup is finite. -/ -instance : Fintype (torsion K) := by - refine @Fintype.ofFinite _ (Set.finite_coe_iff.mpr ?_) +instance : Finite (torsion K) := by refine Set.Finite.of_finite_image ?_ (coe_injective K).injOn refine (Embeddings.finite_of_norm_le K ℂ 1).subset (fun a ⟨u, ⟨h_tors, h_ua⟩⟩ => ⟨?_, fun φ => ?_⟩) @@ -168,17 +172,18 @@ instance : Fintype (torsion K) := by instance : IsCyclic (torsion K) := isCyclic_subgroup_units _ /-- The order of the torsion subgroup. -/ -def torsionOrder : ℕ := Fintype.card (torsion K) +def torsionOrder : ℕ := Nat.card (torsion K) -instance : NeZero (torsionOrder K) := - inferInstanceAs (NeZero (Fintype.card (torsion K))) +theorem torsionOrder_pos : 0 < torsionOrder K := + Nat.card_pos -theorem torsionOrder_ne_zero : - torsionOrder K ≠ 0 := NeZero.ne (torsionOrder K) +theorem torsionOrder_ne_zero : torsionOrder K ≠ 0 := + (torsionOrder_pos K).ne' -theorem torsionOrder_pos : - 0 < torsionOrder K := Nat.pos_of_neZero (torsionOrder K) +instance : NeZero (torsionOrder K) := + ⟨torsionOrder_ne_zero K⟩ +omit [NumberField K] in /-- If `k` does not divide `torsionOrder` then there are no nontrivial roots of unity of order dividing `k`. -/ theorem rootsOfUnity_eq_one {k : ℕ+} (hc : Nat.Coprime k (torsionOrder K)) @@ -191,7 +196,7 @@ theorem rootsOfUnity_eq_one {k : ℕ+} (hc : Nat.Coprime k (torsionOrder K)) rw [torsion, CommGroup.mem_torsion, isOfFinOrder_iff_pow_eq_one] exact ⟨k, k.prop, h⟩ rw [orderOf_submonoid (⟨ζ, hζ⟩ : torsion K)] - exact orderOf_dvd_card + apply orderOf_dvd_natCard /-- The group of roots of unity of order dividing `torsionOrder` is equal to the torsion group. -/ @@ -202,7 +207,7 @@ theorem rootsOfUnity_eq_torsion : refine ⟨fun h => ?_, fun h => ?_⟩ · rw [CommGroup.mem_torsion, isOfFinOrder_iff_pow_eq_one] exact ⟨torsionOrder K, torsionOrder_pos K, h⟩ - · exact Subtype.ext_iff.mp (@pow_card_eq_one (torsion K) _ _ ⟨ζ, h⟩) + · exact Subtype.ext_iff.mp (@pow_card_eq_one' (torsion K) _ ⟨ζ, h⟩) /-- The image of `torsion K` by a complex embedding is the group of complex roots of unity of @@ -215,14 +220,13 @@ theorem map_complexEmbedding_torsion (φ : K →+* ℂ) : exact map_rootsOfUnity _ (torsionOrder K) · let e := ((torsion K).equivMapOfInjective (Units.complexEmbedding φ) (Units.complexEmbedding_injective φ)).symm.toEquiv - rw [Nat.card_eq_fintype_card, Complex.card_rootsOfUnity, Nat.card_congr e, torsionOrder, - Nat.card_eq_fintype_card] + rw [Complex.card_rootsOfUnity, Nat.card_congr e, torsionOrder] theorem even_torsionOrder : Even (torsionOrder K) := by suffices orderOf (⟨-1, neg_one_mem_torsion⟩ : torsion K) = 2 by rw [even_iff_two_dvd, ← this] - exact orderOf_dvd_card + apply orderOf_dvd_natCard rw [← Subgroup.orderOf_coe, ← orderOf_units, Units.val_neg, val_one, orderOf_neg_one, ringChar.eq_zero, if_neg (by decide)] @@ -246,6 +250,8 @@ theorem torsion_eq_one_or_neg_one_of_odd_finrank theorem torsionOrder_eq_two_of_odd_finrank (h : Odd (Module.finrank ℚ K)) : torsionOrder K = 2 := by classical + let := Fintype.ofFinite (torsion K) + rw [torsionOrder, Nat.card_eq_fintype_card] refine (Finset.card_eq_two.2 ⟨1, ⟨-1, neg_one_mem_torsion⟩, by simp [← Subtype.coe_ne_coe], Finset.ext fun x ↦ ⟨fun _ ↦ ?_, fun _ ↦ Finset.mem_univ _⟩⟩) rw [Finset.mem_insert, Finset.mem_singleton, ← Subtype.val_inj, ← Subtype.val_inj] diff --git a/Mathlib/NumberTheory/NumberField/Units/DirichletTheorem.lean b/Mathlib/NumberTheory/NumberField/Units/DirichletTheorem.lean index d39044e1cfc65e..c9a4d626332a35 100644 --- a/Mathlib/NumberTheory/NumberField/Units/DirichletTheorem.lean +++ b/Mathlib/NumberTheory/NumberField/Units/DirichletTheorem.lean @@ -12,6 +12,7 @@ public import Mathlib.NumberTheory.NumberField.Units.Basic /-! # Dirichlet theorem on the group of units of a number field + This file is devoted to the proof of Dirichlet unit theorem that states that the group of units `(𝓞 K)ˣ` of units of the ring of integers `𝓞 K` of a number field `K` modulo its torsion subgroup is a free `ℤ`-module of rank `card (InfinitePlace K) - 1`. diff --git a/Mathlib/NumberTheory/Padics/Hensel.lean b/Mathlib/NumberTheory/Padics/Hensel.lean index 98571de58969ce..9031f81aa319ec 100644 --- a/Mathlib/NumberTheory/Padics/Hensel.lean +++ b/Mathlib/NumberTheory/Padics/Hensel.lean @@ -227,6 +227,7 @@ private def calc_eval_z' {z z' z1 : ℤ_[p]} (hz' : z' = z - z1) {n} (hz : ih n _ = -F.aeval z := by simp only [mul_div_cancel₀ _ hdzne', Subtype.coe_eta] exact ⟨q, by simpa [sub_eq_add_neg, neg_mul_eq_mul_neg, this, hz'] using hq⟩ +set_option linter.defProp false in private def calc_eval_z'_norm {z z' z1 : ℤ_[p]} {n} (hz : ih n z) {q} (heq : F.aeval z' = q * z1 ^ 2) (h1 : ‖(↑(F.aeval z) : ℚ_[p]) / ↑(F.derivative.aeval z)‖ ≤ 1) (hzeq : z1 = ⟨_, h1⟩) : diff --git a/Mathlib/NumberTheory/PrimeCounting.lean b/Mathlib/NumberTheory/PrimeCounting.lean index b88f94d8aa0873..094ba7830c3724 100644 --- a/Mathlib/NumberTheory/PrimeCounting.lean +++ b/Mathlib/NumberTheory/PrimeCounting.lean @@ -193,6 +193,14 @@ lemma primesLE_eq_filter_Icc_zero (n : ℕ) : primesLE n = filter Nat.Prime (Icc ext p simp [primesLE_eq_filter_range] +lemma primesBelow_eq_filter_Ioo_zero (n : ℕ) : primesBelow n = filter Nat.Prime (Ioo 0 n) := by + ext p + simp +contextual [primesBelow_eq_filter_range, Nat.Prime.pos] + +lemma primesLE_eq_filter_Ioc_zero (n : ℕ) : primesLE n = filter Nat.Prime (Ioc 0 n) := by + ext p + simp +contextual [primesLE_eq_filter_range, Nat.Prime.pos] + lemma primesBelow_eq_filter_Ico_one (n : ℕ) : primesBelow n = filter Nat.Prime (Ico 1 n) := by ext p simp +contextual [primesBelow_eq_filter_range, Nat.Prime.one_le] @@ -201,6 +209,14 @@ lemma primesLE_eq_filter_Icc_one (n : ℕ) : primesLE n = filter Nat.Prime (Icc ext p simp +contextual [primesLE_eq_filter_range, Nat.Prime.one_le] +lemma primesBelow_eq_filter_Ioo_one (n : ℕ) : primesBelow n = filter Nat.Prime (Ioo 1 n) := by + ext p + simp +contextual [primesBelow_eq_filter_range, Nat.Prime.one_lt] + +lemma primesLE_eq_filter_Ioc_one (n : ℕ) : primesLE n = filter Nat.Prime (Ioc 1 n) := by + ext p + simp +contextual [primesLE_eq_filter_range, Nat.Prime.one_lt] + lemma primesBelow_eq_filter_Ico_two (n : ℕ) : primesBelow n = filter Nat.Prime (Ico 2 n) := by ext p simp +contextual [primesBelow_eq_filter_range, Nat.Prime.two_le] diff --git a/Mathlib/NumberTheory/Primorial.lean b/Mathlib/NumberTheory/Primorial.lean index 4b40516e23a162..147e83d499d4d4 100644 --- a/Mathlib/NumberTheory/Primorial.lean +++ b/Mathlib/NumberTheory/Primorial.lean @@ -43,6 +43,10 @@ local notation x "#" => primorial x lemma primorial_eq_prod_primesLE (n : ℕ) : n # = ∏ p ∈ primesLE n, p := rfl +lemma primeFactors_primorial (n : ℕ) : primeFactors (n#) = primesLE n := by + rw [primorial_eq_prod_primesLE] + exact primeFactors_prod fun _ hp ↦ prime_of_mem_primesLE hp + @[simp] theorem primorial_zero : 0 # = 1 := by decide @[simp] theorem primorial_one : 1 # = 1 := by decide @@ -52,6 +56,8 @@ lemma primorial_eq_prod_primesLE (n : ℕ) : n # = ∏ p ∈ primesLE n, p := rf theorem primorial_pos (n : ℕ) : 0 < n# := prod_pos fun _p hp ↦ (mem_filter.1 hp).2.pos +lemma primorial_ne_zero (n : ℕ) : n# ≠ 0 := (primorial_pos n).ne' + theorem primorial_mono {m n : ℕ} (h : m ≤ n) : m# ≤ n# := prod_le_prod_of_subset_of_one_le' (by gcongr) (by grind) @@ -139,3 +145,10 @@ theorem primorial_le_four_pow (n : ℕ) : n# ≤ 4 ^ n := by · exact (primorial_lt_four_pow n hn).le @[deprecated (since := "2026-03-21")] alias primorial_le_4_pow := primorial_le_four_pow + +lemma squarefree_primorial (n : ℕ) : Squarefree (n#) := by + rw [primorial_eq_prod_primesLE] + refine Finset.squarefree_prod_of_pairwise_isCoprime (fun _ hp _ hq hpq ↦ ?_) + fun _ hp ↦ (prime_of_mem_primesLE hp).squarefree + simp only [← coprime_iff_isRelPrime] + exact (coprime_primes (prime_of_mem_primesLE hp) (prime_of_mem_primesLE hq)).mpr hpq diff --git a/Mathlib/NumberTheory/RamificationInertia/Basic.lean b/Mathlib/NumberTheory/RamificationInertia/Basic.lean index f6a8da54d09d38..3ff906756fa8ad 100644 --- a/Mathlib/NumberTheory/RamificationInertia/Basic.lean +++ b/Mathlib/NumberTheory/RamificationInertia/Basic.lean @@ -271,12 +271,12 @@ end FinrankQuotientMap section FactLeComap -local notation "e" => ramificationIdx p P +local notation "e" => ramificationIdx' p P /-- `R / p` has a canonical map to `S / (P ^ e)`, where `e` is the ramification index of `P` over `p`. -/ noncomputable instance Quotient.algebraQuotientPowRamificationIdx : Algebra (R ⧸ p) (S ⧸ P ^ e) := - Quotient.algebraQuotientOfLEComap (Ideal.map_le_iff_le_comap.mp le_pow_ramificationIdx) + Quotient.algebraQuotientOfLEComap (Ideal.map_le_iff_le_comap.mp le_pow_ramificationIdx') @[simp] theorem Quotient.algebraMap_quotient_pow_ramificationIdx (x : R) : @@ -289,7 +289,7 @@ This can't be an instance since the map `f : R → S` is generally not inferable @[instance_reducible] def Quotient.algebraQuotientOfRamificationIdxNeZero [hfp : NeZero e] : Algebra (R ⧸ p) (S ⧸ P) := - Quotient.algebraQuotientOfLEComap (le_comap_of_ramificationIdx_ne_zero hfp.out) + Quotient.algebraQuotientOfLEComap (le_comap_of_ramificationIdx'_ne_zero hfp.out) attribute [local instance] Ideal.Quotient.algebraQuotientOfRamificationIdxNeZero @@ -336,7 +336,7 @@ theorem quotientToQuotientRangePowQuotSuccAux_mk {i : ℕ} {a : S} (a_mem : a apply Quotient.map'_mk'' section -variable [hfp : NeZero (ramificationIdx p P)] +variable [hfp : NeZero (ramificationIdx' p P)] /-- `S ⧸ P` embeds into the quotient by `P^(i+1) ⧸ P^e` as a subspace of `P^i ⧸ P^e`. -/ noncomputable def quotientToQuotientRangePowQuotSucc @@ -502,13 +502,13 @@ instance Factors.isPrime (P : (factors (map (algebraMap R S) p)).toFinset) : open scoped Classical in theorem Factors.ramificationIdx_ne_zero (P : (factors (map (algebraMap R S) p)).toFinset) : - ramificationIdx p P.1 ≠ 0 := - IsDedekindDomain.ramificationIdx_ne_zero (ne_zero_of_mem_factors (Multiset.mem_toFinset.mp P.2)) + ramificationIdx' p P.1 ≠ 0 := + IsDedekindDomain.ramificationIdx'_ne_zero (ne_zero_of_mem_factors (Multiset.mem_toFinset.mp P.2)) (Factors.isPrime p P) (Ideal.le_of_dvd (dvd_of_mem_factors (Multiset.mem_toFinset.mp P.2))) open scoped Classical in instance Factors.fact_ramificationIdx_neZero (P : (factors (map (algebraMap R S) p)).toFinset) : - NeZero (ramificationIdx p P.1) := + NeZero (ramificationIdx' p P.1) := ⟨Factors.ramificationIdx_ne_zero p P⟩ attribute [local instance] Quotient.algebraQuotientOfRamificationIdxNeZero @@ -526,15 +526,15 @@ instance Factors.liesOver [p.IsMaximal] (P : (factors (map (algebraMap R S) p)). open scoped Classical in theorem Factors.finrank_pow_ramificationIdx [p.IsMaximal] (P : (factors (map (algebraMap R S) p)).toFinset) : - finrank (R ⧸ p) (S ⧸ (P : Ideal S) ^ ramificationIdx p P.1) = - ramificationIdx p P.1 * inertiaDeg p (P : Ideal S) := by + finrank (R ⧸ p) (S ⧸ (P : Ideal S) ^ ramificationIdx' p P.1) = + ramificationIdx' p P.1 * inertiaDeg p (P : Ideal S) := by rw [finrank_prime_pow_ramificationIdx, inertiaDeg_algebraMap] exacts [Factors.ne_bot p P, NeZero.ne _] open scoped Classical in instance Factors.finiteDimensional_quotient_pow [Module.Finite R S] [p.IsMaximal] (P : (factors (map (algebraMap R S) p)).toFinset) : - FiniteDimensional (R ⧸ p) (S ⧸ (P : Ideal S) ^ ramificationIdx p P.1) := by + FiniteDimensional (R ⧸ p) (S ⧸ (P : Ideal S) ^ ramificationIdx' p P.1) := by refine .of_finrank_pos ?_ rw [pos_iff_ne_zero, Factors.finrank_pow_ramificationIdx] exact mul_ne_zero (Factors.ramificationIdx_ne_zero p P) (inertiaDeg_pos p P.1).ne' @@ -547,14 +547,14 @@ factors in `S` as `∏ i, P i ^ e i`, then `S ⧸ I` factors as `Π i, R ⧸ (P noncomputable def Factors.piQuotientEquiv (p : Ideal R) (hp : map (algebraMap R S) p ≠ ⊥) : S ⧸ map (algebraMap R S) p ≃+* ∀ P : (factors (map (algebraMap R S) p)).toFinset, - S ⧸ (P : Ideal S) ^ ramificationIdx p P.1 := + S ⧸ (P : Ideal S) ^ ramificationIdx' p P.1 := (IsDedekindDomain.quotientEquivPiFactors hp).trans <| @RingEquiv.piCongrRight (factors (map (algebraMap R S) p)).toFinset (fun P => S ⧸ (P : Ideal S) ^ (factors (map (algebraMap R S) p)).count (P : Ideal S)) - (fun P => S ⧸ (P : Ideal S) ^ ramificationIdx p P.1) _ _ + (fun P => S ⧸ (P : Ideal S) ^ ramificationIdx' p P.1) _ _ fun P : (factors (map (algebraMap R S) p)).toFinset => Ideal.quotEquivOfEq <| by - rw [IsDedekindDomain.ramificationIdx_eq_factors_count hp (Factors.isPrime p P) + rw [IsDedekindDomain.ramificationIdx'_eq_factors_count hp (Factors.isPrime p P) (Factors.ne_bot p P)] @[simp] @@ -575,7 +575,7 @@ then `S ⧸ I` factors `R ⧸ I`-linearly as `Π i, R ⧸ (P i ^ e i)`. -/ noncomputable def Factors.piQuotientLinearEquiv (p : Ideal R) (hp : map (algebraMap R S) p ≠ ⊥) : (S ⧸ map (algebraMap R S) p) ≃ₗ[R ⧸ p] ∀ P : (factors (map (algebraMap R S) p)).toFinset, - S ⧸ (P : Ideal S) ^ ramificationIdx p P.1 := + S ⧸ (P : Ideal S) ^ ramificationIdx' p P.1 := { Factors.piQuotientEquiv p hp with map_smul' := by rintro ⟨c⟩ ⟨x⟩; ext P @@ -595,8 +595,8 @@ here `S` is a finite `R`-module (and thus `Frac(S) : Frac(R)` is a finite extens is maximal. -/ theorem sum_ramification_inertia {p : Ideal R} [p.IsMaximal] (hp0 : p ≠ ⊥) : ∑ P ∈ IsDedekindDomain.primesOverFinset p S, - ramificationIdx p P * inertiaDeg p P = finrank K L := by - set e := ramificationIdx p (S := S) + ramificationIdx' p P * inertiaDeg p P = finrank K L := by + set e := ramificationIdx' p (S := S) calc ∑ P ∈ (factors (map (algebraMap R S) p)).toFinset, e P * inertiaDeg p P = ∑ P ∈ (factors (map (algebraMap R S) p)).toFinset.attach, @@ -622,11 +622,11 @@ theorem inertiaDeg_le_finrank [NoZeroSMulDivisors R S] {p : Ideal R} [p.IsMaxima (IsDedekindDomain.mem_primesOverFinset_iff hp0 _).mpr ⟨hP₁, hP₂⟩ rw [← sum_ramification_inertia S K L hp0, ← Finset.add_sum_erase _ _ hP] refine le_trans (Nat.le_mul_of_pos_left _ ?_) (Nat.le_add_right _ _) - exact Nat.pos_iff_ne_zero.mpr <| IsDedekindDomain.ramificationIdx_ne_zero_of_liesOver _ hp0 + exact Nat.pos_iff_ne_zero.mpr <| IsDedekindDomain.ramificationIdx'_ne_zero_of_liesOver _ hp0 theorem ramificationIdx_le_finrank [NoZeroSMulDivisors R S] {p : Ideal R} [p.IsMaximal] (P : Ideal S) [hP₁ : P.IsPrime] [hP₂ : P.LiesOver p] : - p.ramificationIdx P ≤ Module.finrank K L := by + p.ramificationIdx' P ≤ Module.finrank K L := by classical by_cases hp0 : p = ⊥ · simp [hp0] @@ -643,13 +643,13 @@ theorem card_primesOverFinset_le_finrank [NoZeroSMulDivisors R S] {p : Ideal R} have : P.IsPrime := ((IsDedekindDomain.mem_primesOverFinset_iff hp0 _).mp hP).1 have : P.LiesOver p := ((IsDedekindDomain.mem_primesOverFinset_iff hp0 _).mp hP).2 refine Right.one_le_mul ?_ ?_ - · exact Nat.pos_iff_ne_zero.mpr <| IsDedekindDomain.ramificationIdx_ne_zero_of_liesOver _ hp0 + · exact Nat.pos_iff_ne_zero.mpr <| IsDedekindDomain.ramificationIdx'_ne_zero_of_liesOver _ hp0 · exact Nat.pos_iff_ne_zero.mpr <| inertiaDeg_ne_zero p P /-- `Ideal.sum_ramification_inertia`, in the local (DVR) case. -/ lemma ramificationIdx_mul_inertiaDeg_of_isLocalRing [IsLocalRing S] {p : Ideal R} [p.IsMaximal] (hp0 : p ≠ ⊥) : - ramificationIdx p (IsLocalRing.maximalIdeal S) * + ramificationIdx' p (IsLocalRing.maximalIdeal S) * p.inertiaDeg (IsLocalRing.maximalIdeal S) = Module.finrank K L := by have := FaithfulSMul.of_field_isFractionRing R S K L simp_rw [← sum_ramification_inertia S K L hp0, IsLocalRing.primesOverFinset_eq S hp0, diff --git a/Mathlib/NumberTheory/RamificationInertia/Galois.lean b/Mathlib/NumberTheory/RamificationInertia/Galois.lean index b0f32f53494438..d4ce38c6669b64 100644 --- a/Mathlib/NumberTheory/RamificationInertia/Galois.lean +++ b/Mathlib/NumberTheory/RamificationInertia/Galois.lean @@ -5,6 +5,7 @@ Authors: Yongle Hu, Jiedong Jiang -/ module +public import Mathlib.RingTheory.Invariant.Galois public import Mathlib.RingTheory.RamificationInertia.Basic /-! @@ -57,7 +58,7 @@ open scoped Classical in maximal ideal `p` of `A` are the same, which we define as `Ideal.ramificationIdxIn`. -/ noncomputable def ramificationIdxIn {A : Type*} [CommRing A] (p : Ideal A) (B : Type*) [CommRing B] [Algebra A B] : ℕ := - if h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p then h.choose.ramificationIdx' A + if h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p then h.choose.ramificationIdx A else 0 open scoped Classical in @@ -137,9 +138,9 @@ instance isPretransitive_of_isGaloisGroup : MulAction.IsPretransitive G (primesO include p G in /-- All the `Ideal.ramificationIdx` over a fixed maximal ideal are the same. -/ theorem ramificationIdx_eq_of_isGaloisGroup : - P.ramificationIdx' A = Q.ramificationIdx' A := by + P.ramificationIdx A = Q.ramificationIdx A := by rcases exists_smul_eq_of_isGaloisGroup p P Q G with ⟨σ, rfl⟩ - rw [ramificationIdx'_smul] + rw [ramificationIdx_smul] include p G in /-- All the `Ideal.inertiaDeg` over a fixed maximal ideal are the same. -/ @@ -151,7 +152,7 @@ theorem inertiaDeg_eq_of_isGaloisGroup : include p G in /-- The `ramificationIdxIn` is equal to any ramification index over the same ideal. -/ theorem ramificationIdxIn_eq_ramificationIdx : - ramificationIdxIn p B = P.ramificationIdx' A := by + ramificationIdxIn p B = P.ramificationIdx A := by have h : ∃ P : Ideal B, P.IsPrime ∧ P.LiesOver p := ⟨P, hPp, hp⟩ obtain ⟨_, _⟩ := h.choose_spec rw [ramificationIdxIn, dif_pos h] @@ -162,7 +163,7 @@ theorem ramificationIdxIn_ne_zero [Module.Finite A B] [FaithfulSMul A B] {p : Id p.ramificationIdxIn B ≠ 0 := by obtain ⟨P⟩ := (inferInstance : Nonempty (primesOver p B)) rw [ramificationIdxIn_eq_ramificationIdx p P G] - exact (P.1.ramificationIdx'_pos A).ne' + exact (P.1.ramificationIdx_pos A).ne' include G in /-- The `inertiaDegIn` is equal to any ramification index over the same ideal. -/ @@ -202,7 +203,7 @@ theorem ramificationIdxIn_mul_ramificationIdxIn [Flat B C] : obtain ⟨⟨Q, _, hQ⟩⟩ := (inferInstance : Nonempty (primesOver P C)) have : Q.LiesOver p := LiesOver.trans Q P p rw [ramificationIdxIn_eq_ramificationIdx p P G, ramificationIdxIn_eq_ramificationIdx p Q GAC, - ramificationIdxIn_eq_ramificationIdx P Q GBC, ← ramificationIdx'_tower P Q] + ramificationIdxIn_eq_ramificationIdx P Q GBC, ← ramificationIdx_tower P Q] @[deprecated (since := "2026-06-18")] alias ramificationIdxIn_mul_ramificationIdxIn' := ramificationIdxIn_mul_ramificationIdxIn @@ -231,37 +232,45 @@ end fundamental_identity section tower -variable {A B : Type*} [CommRing A] [IsDomain A] [CommRing B] [IsDomain B] - [Algebra A B] [Flat A B] {p : Ideal A} (P : Ideal B) [p.IsPrime] +variable {A B : Type*} [CommRing A] [CommRing B] + [Algebra A B] [FaithfulSMul A B] {p : Ideal A} (P : Ideal B) [P.IsPrime] [P.LiesOver p] (G : Type*) [Group G] [Finite G] [MulSemiringAction G B] [IsGaloisGroup G A B] (C : Type*) [CommRing C] [IsDomain C] [Algebra A C] - [Algebra B C] [Module.Finite A B] [Module.Finite A C] [Module.Finite B C] [Flat A C] - [Flat B C] [IsScalarTower A B C] + [Algebra B C] [FaithfulSMul B C] [IsScalarTower A B C] (GAC : Type*) [Group GAC] [Finite GAC] [MulSemiringAction GAC C] [IsGaloisGroup GAC A C] - (GBC : Type*) [Group GBC] [Finite GBC] [MulSemiringAction GBC C] [IsGaloisGroup GBC B C] --- todo: use transitivity to prove this under much weaker assumptions -include G GAC GBC in +include G GAC in +open IsGaloisGroup MulAction in theorem ncard_primesOver_mul_ncard_primesOver : (p.primesOver B).ncard * (P.primesOver C).ncard = (p.primesOver C).ncard := by - let := IsFractionRing.mulSemiringAction G A B (FractionRing A) (FractionRing B) - let := IsFractionRing.mulSemiringAction GAC A C (FractionRing A) (FractionRing C) - let := IsFractionRing.mulSemiringAction GBC B C (FractionRing B) (FractionRing C) - have : p.ramificationIdxIn C * p.inertiaDegIn C ≠ 0 := - mul_ne_zero (ramificationIdxIn_ne_zero GAC) (inertiaDegIn_ne_zero GAC) - rw [← Nat.mul_left_inj this, ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p C GAC] - calc - _ = ((p.primesOver B).ncard * (p.ramificationIdxIn B * p.inertiaDegIn B)) * - ((P.primesOver C).ncard * (P.ramificationIdxIn C * P.inertiaDegIn C)) := by - rw [← inertiaDegIn_mul_inertiaDegIn p P G C GAC GBC, - ← ramificationIdxIn_mul_ramificationIdxIn P G C GAC GBC] - ring - _ = Nat.card GAC := by - rw [ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn p B G, - ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn P C GBC, - (IsGaloisGroup.toFractionRing G A B).card_eq_finrank, - (IsGaloisGroup.toFractionRing GAC A C).card_eq_finrank, - (IsGaloisGroup.toFractionRing GBC B C).card_eq_finrank, Module.finrank_mul_finrank] + have : Algebra.IsIntegral A C := isInvariant.isIntegral A C GAC + have : Algebra.IsIntegral B C := Algebra.IsIntegral.tower_top A + let f := restrictHom GAC G A B C + let H := (stabilizer G P).comap f + have key (Q Q' : Ideal C) [Q.LiesOver P] [Q'.LiesOver P] g (hg : g • Q = Q') : g ∈ H := by + simpa [← restrictHom_smul_under GAC G A, ← over_def _ P, H] using congr_arg (under B) hg + obtain ⟨Q, _, _⟩ := (inferInstance : Nonempty (P.primesOver C)) + have : Q.LiesOver p := .trans Q P p + have orbit_eq : orbit H Q = P.primesOver C := by + ext Q' + constructor + · rintro ⟨g, rfl : g • Q = Q'⟩ + refine ⟨inferInstance, ?_⟩ + rw [liesOver_iff, H.smul_def, ← restrictHom_smul_under GAC G A B C, ← Q.over_def P] + exact g.2.symm + · rintro ⟨_, _⟩ + have : Q'.LiesOver p := .trans Q' P p + obtain ⟨g, hg⟩ := + IsInvariant.exists_smul_of_under_eq A C GAC Q Q' ((Q.over_def p).symm.trans (Q'.over_def p)) + exact ⟨⟨g, key Q Q' g hg.symm⟩, by simpa [Subgroup.smul_def] using hg.symm⟩ + have stabilizer_eq : stabilizer H Q = (stabilizer GAC Q).subgroupOf H := by + simp [Subgroup.ext_iff, Subgroup.mem_subgroupOf] + rw [← IsInvariant.orbit_eq_primesOver A B G p P, ← index_stabilizer, + ← orbit_eq, ← index_stabilizer, stabilizer_eq, ← Subgroup.relIndex, + ← IsInvariant.orbit_eq_primesOver A C GAC p Q, ← index_stabilizer, + ← (stabilizer G P).index_comap_of_surjective (restrictHom_surjective GAC G A B C), + mul_comm, Subgroup.relIndex_mul_index] + exact key Q Q end tower @@ -279,8 +288,6 @@ theorem card_stabilizer_eq_card_inertia_mul_finrank (p : Ideal R) [p.IsPrime] (P : Ideal S) [P.LiesOver p] [P.IsPrime] [PerfectField p.ResidueField] : Nat.card (MulAction.stabilizer G P) = Nat.card (inertia G P) * P.inertiaDeg' R := by let := Localization.AtPrime.algebraOfLiesOver p P - let : Algebra (R ⧸ p) p.ResidueField := inferInstance - let : Algebra (S ⧸ P) P.ResidueField := inferInstance have heq : (algebraMap (S ⧸ P) P.ResidueField).comp (algebraMap (R ⧸ p) (S ⧸ P)) = (algebraMap p.ResidueField P.ResidueField).comp (algebraMap (R ⧸ p) p.ResidueField) := by ext diff --git a/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean b/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean index d5378c4a94dcc3..75ab4d182a9aad 100644 --- a/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean +++ b/Mathlib/NumberTheory/RamificationInertia/HilbertTheory.lean @@ -283,7 +283,6 @@ private lemma instances (hp : p ≠ ⊥) : exact ⟨inst₁, inst₂, inst₃, inst₄, inst₅, Ideal.ne_bot_of_liesOver_of_ne_bot hp 𝓟D⟩ variable [FiniteDimensional K L] [Ring.HasFiniteQuotients A] [𝓟D.IsMaximal] [P.IsMaximal] - [p.IsMaximal] include K L D P in private lemma ramificationIdxIn_eq_and_inertiaDegIn_eq (hp : p ≠ ⊥) : @@ -294,13 +293,10 @@ private lemma ramificationIdxIn_eq_and_inertiaDegIn_eq (hp : p ≠ ⊥) : · exact Nat.pos_of_ne_zero <| inertiaDegIn_ne_zero (stabilizer Gal(L/K) P) · rw [ramificationIdxIn_eq_ramificationIdx p P Gal(L/K), ramificationIdxIn_eq_ramificationIdx _ P (stabilizer Gal(L/K) P)] - rw [← ramificationIdx_eq_ramificationIdx' p _ hp, - ← ramificationIdx_eq_ramificationIdx' 𝓟D _ h𝓟] - exact IsDedekindDomain.ramificationIdx_le_ramificationIdx _ _ _ hp + exact 𝓟D.ramificationIdx_above_le P · rw [inertiaDegIn_eq_inertiaDeg p P Gal(L/K), inertiaDegIn_eq_inertiaDeg _ P (stabilizer Gal(L/K) P)] - rw [← inertiaDeg_eq_inertiaDeg' p, ← inertiaDeg_eq_inertiaDeg' 𝓟D] - exact inertiaDeg_le_inertiaDeg p 𝓟D P + exact inertiaDeg'_above_le 𝓟D P · have := ncard_primesOver_mul_ramificationIdxIn_mul_inertiaDegIn 𝓟D B (stabilizer Gal(L/K) P) rw [primesOver_eq_singleton K L P D 𝓞D, Set.ncard_singleton, one_mul] at this rw [this, IsGaloisGroup.card_eq_finrank (stabilizer Gal(L/K) P) D L, @@ -330,12 +326,12 @@ Let `D` be the decomposition field of `P` in `L/K`. Let `𝓟D` be a prime ideal then `𝓟D` is unramified over `K`. -/ theorem ramificationIdx_eq (hp : p ≠ ⊥) : - 𝓟D.ramificationIdx' A = 1 := by + 𝓟D.ramificationIdx A = 1 := by obtain ⟨_, _, _, _, _, h𝓟⟩ := instances A K L P D 𝓞D 𝓟D hp - have := ramificationIdx'_tower (R := A) 𝓟D P + have := ramificationIdx_tower (R := A) 𝓟D P rwa [← ramificationIdxIn_eq_ramificationIdx 𝓟D P (stabilizer Gal(L/K) P), ramificationIdxIn_eq A K L P D 𝓞D 𝓟D hp, ramificationIdxIn_eq_ramificationIdx p P Gal(L/K), - right_eq_mul₀ <| (ramificationIdx'_pos P A).ne'] at this + right_eq_mul₀ <| (ramificationIdx_pos P A).ne'] at this include K L D P in /-- diff --git a/Mathlib/NumberTheory/RamificationInertia/Ramification.lean b/Mathlib/NumberTheory/RamificationInertia/Ramification.lean index 7013facf35c31a..4e5a335aecebcf 100644 --- a/Mathlib/NumberTheory/RamificationInertia/Ramification.lean +++ b/Mathlib/NumberTheory/RamificationInertia/Ramification.lean @@ -14,7 +14,7 @@ public import Mathlib.RingTheory.DedekindDomain.Ideal.Lemmas Given `P : Ideal S` lying over `p : Ideal R` for the ring extension `f : R →+* S` (assuming `P` and `p` are prime or maximal where needed), -the **ramification index** `Ideal.ramificationIdx p P` is the multiplicity of `P` in `map f p`. +the **ramification index** `Ideal.ramificationIdx' p P` is the multiplicity of `P` in `map f p`. ## Implementation notes @@ -59,38 +59,44 @@ section DecEq In particular, if `p` is not contained in `P^n`, then the ramification index is 0. -If there is no largest such `n` (e.g. because `p = ⊥`), then `ramificationIdx` is +If there is no largest such `n` (e.g. because `p = ⊥`), then `ramificationIdx'` is defined to be 0. -Note: This definition of ramification index will eventually be replaced by `Ideal.ramificationIdx'`. +Note: This definition of ramification index will eventually be replaced by `Ideal.ramificationIdx`. -/ -noncomputable def ramificationIdx : ℕ := sSup {n | map f p ≤ P ^ n} +noncomputable def ramificationIdx' : ℕ := sSup {n | map f p ≤ P ^ n} variable {p P} -theorem ramificationIdx_eq_find [DecidablePred fun n ↦ ∀ (k : ℕ), map f p ≤ P ^ k → k ≤ n] +theorem ramificationIdx'_eq_find [DecidablePred fun n ↦ ∀ (k : ℕ), map f p ≤ P ^ k → k ≤ n] (h : ∃ n, ∀ k, map f p ≤ P ^ k → k ≤ n) : - ramificationIdx p P = Nat.find h := by + ramificationIdx' p P = Nat.find h := by convert! Nat.sSup_def h -theorem ramificationIdx_eq_zero (h : ∀ n : ℕ, ∃ k, map f p ≤ P ^ k ∧ n < k) : - ramificationIdx p P = 0 := +@[deprecated (since := "2026-07-01")] alias ramificationIdx_eq_find := ramificationIdx'_eq_find + +theorem ramificationIdx'_eq_zero (h : ∀ n : ℕ, ∃ k, map f p ≤ P ^ k ∧ n < k) : + ramificationIdx' p P = 0 := dif_neg (by push Not; exact h) -theorem ramificationIdx_spec {n : ℕ} (hle : map f p ≤ P ^ n) (hgt : ¬map f p ≤ P ^ (n + 1)) : - ramificationIdx p P = n := by +@[deprecated (since := "2026-07-01")] alias ramificationIdx_eq_zero := ramificationIdx'_eq_zero + +theorem ramificationIdx'_spec {n : ℕ} (hle : map f p ≤ P ^ n) (hgt : ¬map f p ≤ P ^ (n + 1)) : + ramificationIdx' p P = n := by classical let Q : ℕ → Prop := fun m => ∀ k : ℕ, map f p ≤ P ^ k → k ≤ m have : Q n := by intro k hk refine le_of_not_gt fun hnk => ?_ exact hgt (hk.trans (Ideal.pow_le_pow_right hnk)) - rw [ramificationIdx_eq_find ⟨n, this⟩] + rw [ramificationIdx'_eq_find ⟨n, this⟩] refine le_antisymm (Nat.find_min' _ this) (le_of_not_gt fun h : Nat.find _ < n => ?_) obtain this' := Nat.find_spec ⟨n, this⟩ exact h.not_ge (this' _ hle) -theorem ramificationIdx_lt {n : ℕ} (hgt : ¬map f p ≤ P ^ n) : ramificationIdx p P < n := by +@[deprecated (since := "2026-07-01")] alias ramificationIdx_spec := ramificationIdx'_spec + +theorem ramificationIdx'_lt {n : ℕ} (hgt : ¬map f p ≤ P ^ n) : ramificationIdx' p P < n := by classical rcases n with - | n · simp at hgt @@ -98,45 +104,66 @@ theorem ramificationIdx_lt {n : ℕ} (hgt : ¬map f p ≤ P ^ n) : ramificationI have : ∀ k, map f p ≤ P ^ k → k ≤ n := by refine fun k hk => le_of_not_gt fun hnk => ?_ exact hgt (hk.trans (Ideal.pow_le_pow_right hnk)) - rw [ramificationIdx_eq_find ⟨n, this⟩] + rw [ramificationIdx'_eq_find ⟨n, this⟩] exact Nat.find_min' ⟨n, this⟩ this +@[deprecated (since := "2026-07-01")] alias ramificationIdx_lt := ramificationIdx'_lt + @[simp] -theorem ramificationIdx_bot : ramificationIdx (⊥ : Ideal R) P = 0 := +theorem ramificationIdx'_bot : ramificationIdx' (⊥ : Ideal R) P = 0 := dif_neg <| not_exists.mpr fun n hn => n.lt_succ_self.not_ge (hn _ (by simp)) +@[deprecated (since := "2026-07-01")] alias ramificationIdx_bot := ramificationIdx'_bot + @[simp] -theorem ramificationIdx_of_not_le (h : ¬map f p ≤ P) : ramificationIdx p P = 0 := - ramificationIdx_spec (by simp) (by simpa using h) +theorem ramificationIdx'_of_not_le (h : ¬map f p ≤ P) : ramificationIdx' p P = 0 := + ramificationIdx'_spec (by simp) (by simpa using h) + +@[deprecated (since := "2026-07-01")] alias ramificationIdx_of_not_le := ramificationIdx'_of_not_le -theorem ramificationIdx_bot' (hp : p ≠ ⊥) (hf : Function.Injective f) : - ramificationIdx p (⊥ : Ideal S) = 0 := - ramificationIdx_of_not_le <| le_bot_iff.not.mpr <| (map_eq_bot_iff_of_injective hf).not.mpr hp +theorem ramificationIdx'_bot' (hp : p ≠ ⊥) (hf : Function.Injective f) : + ramificationIdx' p (⊥ : Ideal S) = 0 := + ramificationIdx'_of_not_le <| le_bot_iff.not.mpr <| (map_eq_bot_iff_of_injective hf).not.mpr hp -theorem ramificationIdx_ne_zero {e : ℕ} (he : e ≠ 0) (hle : map f p ≤ P ^ e) - (hnle : ¬map f p ≤ P ^ (e + 1)) : ramificationIdx p P ≠ 0 := by - rwa [ramificationIdx_spec hle hnle] +@[deprecated (since := "2026-07-01")] alias ramificationIdx_bot' := ramificationIdx'_bot' -theorem le_pow_of_le_ramificationIdx {n : ℕ} (hn : n ≤ ramificationIdx p P) : +theorem ramificationIdx'_ne_zero {e : ℕ} (he : e ≠ 0) (hle : map f p ≤ P ^ e) + (hnle : ¬map f p ≤ P ^ (e + 1)) : ramificationIdx' p P ≠ 0 := by + rwa [ramificationIdx'_spec hle hnle] + +@[deprecated (since := "2026-07-01")] alias ramificationIdx_ne_zero := ramificationIdx'_ne_zero + +theorem le_pow_of_le_ramificationIdx' {n : ℕ} (hn : n ≤ ramificationIdx' p P) : map f p ≤ P ^ n := by contrapose! hn - exact ramificationIdx_lt hn + exact ramificationIdx'_lt hn + +@[deprecated (since := "2026-07-01")] alias le_pow_of_le_ramificationIdx := + le_pow_of_le_ramificationIdx' + +theorem le_pow_ramificationIdx' : map f p ≤ P ^ ramificationIdx' p P := + le_pow_of_le_ramificationIdx' (le_refl _) + +@[deprecated (since := "2026-07-01")] alias le_pow_ramificationIdx := le_pow_ramificationIdx' + +theorem le_comap_pow_ramificationIdx' : p ≤ comap f (P ^ ramificationIdx' p P) := + map_le_iff_le_comap.mp le_pow_ramificationIdx' -theorem le_pow_ramificationIdx : map f p ≤ P ^ ramificationIdx p P := - le_pow_of_le_ramificationIdx (le_refl _) +@[deprecated (since := "2026-07-01")] alias le_comap_pow_ramificationIdx := + le_comap_pow_ramificationIdx' -theorem le_comap_pow_ramificationIdx : p ≤ comap f (P ^ ramificationIdx p P) := - map_le_iff_le_comap.mp le_pow_ramificationIdx +theorem le_comap_of_ramificationIdx'_ne_zero (h : ramificationIdx' p P ≠ 0) : p ≤ comap f P := + Ideal.map_le_iff_le_comap.mp <| le_pow_ramificationIdx'.trans <| Ideal.pow_le_self <| h -theorem le_comap_of_ramificationIdx_ne_zero (h : ramificationIdx p P ≠ 0) : p ≤ comap f P := - Ideal.map_le_iff_le_comap.mp <| le_pow_ramificationIdx.trans <| Ideal.pow_le_self <| h +@[deprecated (since := "2026-07-01")] alias le_comap_of_ramificationIdx_ne_zero := + le_comap_of_ramificationIdx'_ne_zero variable {S₁ : Type*} [CommRing S₁] [Algebra R S₁] variable (p) in -lemma ramificationIdx_comap_eq (e : S ≃ₐ[R] S₁) (P : Ideal S₁) : - ramificationIdx p (P.comap e) = ramificationIdx p P := by - dsimp only [ramificationIdx] +lemma ramificationIdx'_comap_eq (e : S ≃ₐ[R] S₁) (P : Ideal S₁) : + ramificationIdx' p (P.comap e) = ramificationIdx' p P := by + dsimp only [ramificationIdx'] congr 1 ext n simp only [Set.mem_setOf_eq, Ideal.map_le_iff_le_comap] @@ -145,20 +172,24 @@ lemma ramificationIdx_comap_eq (e : S ≃ₐ[R] S₁) (P : Ideal S₁) : comap_comap, RingEquiv.symm_symm, e.toRingEquiv_toRingHom, ← e.toAlgHom_toRingHom, AlgHom.comp_algebraMap] +@[deprecated (since := "2026-07-01")] alias ramificationIdx_comap_eq := ramificationIdx'_comap_eq + variable (p) in -lemma ramificationIdx_map_eq {E : Type*} [EquivLike E S S₁] [AlgEquivClass E R S S₁] +lemma ramificationIdx'_map_eq {E : Type*} [EquivLike E S S₁] [AlgEquivClass E R S S₁] (P : Ideal S) (e : E) : - ramificationIdx p (P.map e) = ramificationIdx p P := by + ramificationIdx' p (P.map e) = ramificationIdx' p P := by rw [show P.map e = _ from P.map_comap_of_equiv (RingEquivClass.toRingEquiv e : S ≃+* S₁)] - exact p.ramificationIdx_comap_eq (AlgEquivClass.toAlgEquiv e).symm P + exact p.ramificationIdx'_comap_eq (AlgEquivClass.toAlgEquiv e).symm P + +@[deprecated (since := "2026-07-01")] alias ramificationIdx_map_eq := ramificationIdx'_map_eq -lemma ramificationIdx_ne_one_iff (hp : map f p ≤ P) : - ramificationIdx p P ≠ 1 ↔ p.map f ≤ P ^ 2 := by +lemma ramificationIdx'_ne_one_iff (hp : map f p ≤ P) : + ramificationIdx' p P ≠ 1 ↔ p.map f ≤ P ^ 2 := by classical by_cases! H : ∀ n : ℕ, ∃ k, p.map f ≤ P ^ k ∧ n < k · obtain ⟨k, hk, h2k⟩ := H 2 - simp [Ideal.ramificationIdx_eq_zero H, hk.trans (Ideal.pow_le_pow_right h2k.le)] - rw [Ideal.ramificationIdx_eq_find H] + simp [Ideal.ramificationIdx'_eq_zero H, hk.trans (Ideal.pow_le_pow_right h2k.le)] + rw [Ideal.ramificationIdx'_eq_find H] constructor · intro he have : 1 ≤ Nat.find H := Nat.find_spec H 1 (by simpa) @@ -170,15 +201,18 @@ lemma ramificationIdx_ne_one_iff (hp : map f p ≤ P) : have := Nat.find_spec H 2 he lia +@[deprecated (since := "2026-07-01")] alias ramificationIdx_ne_one_iff := + ramificationIdx'_ne_one_iff + open IsLocalRing in /-- The converse is true when `S` is a Dedekind domain. -See `Ideal.ramificationIdx_eq_one_iff_of_isDedekindDomain`. -/ -lemma ramificationIdx_eq_one_of_map_localization +See `Ideal.ramificationIdx'_eq_one_iff_of_isDedekindDomain`. -/ +lemma ramificationIdx'_eq_one_of_map_localization {p : Ideal R} {P : Ideal S} [P.IsPrime] [IsNoetherianRing S] (hpP : map (algebraMap R S) p ≤ P) (hp : P ≠ ⊥) (hp' : P.primeCompl ≤ nonZeroDivisors S) (H : p.map (algebraMap R (Localization.AtPrime P)) = maximalIdeal (Localization.AtPrime P)) : - ramificationIdx p P = 1 := by - rw [← not_ne_iff (b := 1), Ideal.ramificationIdx_ne_one_iff hpP] + ramificationIdx' p P = 1 := by + rw [← not_ne_iff (b := 1), Ideal.ramificationIdx'_ne_one_iff hpP] intro h₂ replace h₂ := Ideal.map_mono («f» := algebraMap S (Localization.AtPrime P)) h₂ rw [Ideal.map_pow, Localization.AtPrime.map_eq_maximalIdeal, Ideal.map_map, @@ -189,82 +223,96 @@ lemma ramificationIdx_eq_one_of_map_localization · exact hp this · exact IsLocalization.injective _ hp' -theorem ramificationIdx_map_self_eq_one [IsDedekindDomain S] (h₁ : map f p ≠ ⊤) (h₂ : map f p ≠ ⊥) : - ramificationIdx p (map f p) = 1 := by - refine ramificationIdx_spec (by simp) fun h ↦ ?_ +@[deprecated (since := "2026-07-01")] alias ramificationIdx_eq_one_of_map_localization := + ramificationIdx'_eq_one_of_map_localization + +theorem ramificationIdx'_map_self_eq_one [IsDedekindDomain S] + (h₁ : map f p ≠ ⊤) (h₂ : map f p ≠ ⊥) : ramificationIdx' p (map f p) = 1 := by + refine ramificationIdx'_spec (by simp) fun h ↦ ?_ have : map f p ^ 1 = (map f p) ^ 2 := by rw [pow_one] exact le_antisymm h <| pow_le_self two_ne_zero have := IsMulTorsionFree.pow_right_injective₀ (by rwa [one_eq_top]) h₂ this simp_all +@[deprecated (since := "2026-07-01")] alias ramificationIdx_map_self_eq_one := + ramificationIdx'_map_self_eq_one + variable (p P) in -theorem ramificationIdx_le_ramificationIdx {T : Type*} [CommRing T] [Algebra R T] +theorem ramificationIdx'_le_ramificationIdx' {T : Type*} [CommRing T] [Algebra R T] [Algebra S T] [IsScalarTower R S T] (Q : Ideal T) (hp : p = comap f P) - (h : ramificationIdx p Q ≠ 0) : ramificationIdx P Q ≤ ramificationIdx p Q := by - simp_rw [ramificationIdx, Ne] at * + (h : ramificationIdx' p Q ≠ 0) : ramificationIdx' P Q ≤ ramificationIdx' p Q := by + simp_rw [ramificationIdx', Ne] at * refine csSup_le_csSup' (h.imp_symm Nat.sSup_of_not_bddAbove) fun n hn ↦ ?_ simp_rw [hp, IsScalarTower.algebraMap_eq R S T, ← map_map, map_le_iff_le_comap] exact comap_mono <| by rwa [← map_le_iff_le_comap] +@[deprecated (since := "2026-07-01")] alias ramificationIdx_le_ramificationIdx := + ramificationIdx'_le_ramificationIdx' + namespace IsDedekindDomain variable [IsDedekindDomain S] -theorem ramificationIdx_eq_normalizedFactors_count +theorem ramificationIdx'_eq_normalizedFactors_count (hp0 : map f p ≠ ⊥) (hP : P.IsPrime) - (hP0 : P ≠ ⊥) : ramificationIdx p P = (normalizedFactors (map f p)).count P := by + (hP0 : P ≠ ⊥) : ramificationIdx' p P = (normalizedFactors (map f p)).count P := by have hPirr := (Ideal.prime_of_isPrime hP0 hP).irreducible - refine ramificationIdx_spec (Ideal.le_of_dvd ?_) (mt Ideal.dvd_iff_le.mpr ?_) <;> + refine ramificationIdx'_spec (Ideal.le_of_dvd ?_) (mt Ideal.dvd_iff_le.mpr ?_) <;> rw [dvd_iff_normalizedFactors_le_normalizedFactors (pow_ne_zero _ hP0) hp0, normalizedFactors_pow, normalizedFactors_irreducible hPirr, normalize_eq, Multiset.nsmul_singleton, ← Multiset.le_count_iff_replicate_le] exact (Nat.lt_succ_self _).not_ge -theorem ramificationIdx_eq_multiplicity (hp : map f p ≠ ⊥) (hP : P.IsPrime) : - ramificationIdx p P = multiplicity P (Ideal.map f p) := by +theorem ramificationIdx'_eq_multiplicity (hp : map f p ≠ ⊥) (hP : P.IsPrime) : + ramificationIdx' p P = multiplicity P (Ideal.map f p) := by classical by_cases hP₂ : P = ⊥ · rw [hP₂, ← Ideal.zero_eq_bot, multiplicity_zero_eq_zero_of_ne_zero _ hp] - exact Ideal.ramificationIdx_of_not_le (mt le_bot_iff.mp hp) + exact Ideal.ramificationIdx'_of_not_le (mt le_bot_iff.mp hp) rw [multiplicity_eq_of_emultiplicity_eq_some] - rw [ramificationIdx_eq_normalizedFactors_count hp hP hP₂, ← normalize_eq P, + rw [ramificationIdx'_eq_normalizedFactors_count hp hP hP₂, ← normalize_eq P, ← UniqueFactorizationMonoid.emultiplicity_eq_count_normalizedFactors _ hp, normalize_eq] exact irreducible_iff_prime.mpr <| prime_of_isPrime hP₂ hP -theorem ramificationIdx_eq_factors_count +theorem ramificationIdx'_eq_factors_count (hp0 : map f p ≠ ⊥) (hP : P.IsPrime) (hP0 : P ≠ ⊥) : - ramificationIdx p P = (factors (map f p)).count P := by - rw [IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count hp0 hP hP0, + ramificationIdx' p P = (factors (map f p)).count P := by + rw [IsDedekindDomain.ramificationIdx'_eq_normalizedFactors_count hp0 hP hP0, factors_eq_normalizedFactors] -theorem ramificationIdx_ne_zero (hp0 : map f p ≠ ⊥) (hP : P.IsPrime) (le : map f p ≤ P) : - ramificationIdx p P ≠ 0 := by +theorem ramificationIdx'_ne_zero (hp0 : map f p ≠ ⊥) (hP : P.IsPrime) (le : map f p ≤ P) : + ramificationIdx' p P ≠ 0 := by classical have hP0 : P ≠ ⊥ := by rintro rfl exact hp0 (le_bot_iff.mp le) have hPirr := (Ideal.prime_of_isPrime hP0 hP).irreducible - rw [IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count hp0 hP hP0] + rw [IsDedekindDomain.ramificationIdx'_eq_normalizedFactors_count hp0 hP hP0] obtain ⟨P', hP', P'_eq⟩ := exists_mem_normalizedFactors_of_dvd hp0 hPirr (Ideal.dvd_iff_le.mpr le) rwa [Multiset.count_ne_zero, associated_iff_eq.mp P'_eq] -theorem ramificationIdx_ne_zero_of_liesOver [IsDomain R] [IsTorsionFree R S] +@[deprecated (since := "2026-07-01")] alias ramificationIdx_ne_zero := ramificationIdx'_ne_zero + +theorem ramificationIdx'_ne_zero_of_liesOver [IsDomain R] [IsTorsionFree R S] (P : Ideal S) [hP : P.IsPrime] {p : Ideal R} (hp : p ≠ ⊥) [hPp : P.LiesOver p] : - ramificationIdx p P ≠ 0 := - IsDedekindDomain.ramificationIdx_ne_zero (map_ne_bot_of_ne_bot hp) hP <| + ramificationIdx' p P ≠ 0 := + IsDedekindDomain.ramificationIdx'_ne_zero (map_ne_bot_of_ne_bot hp) hP <| map_le_iff_le_comap.mpr <| le_of_eq <| (liesOver_iff _ _).mp hPp +@[deprecated (since := "2026-07-01")] alias ramificationIdx_ne_zero_of_liesOver := + ramificationIdx'_ne_zero_of_liesOver + open IsLocalRing in -lemma ramificationIdx_eq_one_iff +lemma ramificationIdx'_eq_one_iff {p : Ideal R} {P : Ideal S} [P.IsPrime] (hp : P ≠ ⊥) (hpP : p.map (algebraMap R S) ≤ P) : - ramificationIdx p P = 1 ↔ + ramificationIdx' p P = 1 ↔ p.map (algebraMap R (Localization.AtPrime P)) = maximalIdeal (Localization.AtPrime P) := by - refine ⟨?_, ramificationIdx_eq_one_of_map_localization hpP hp (primeCompl_le_nonZeroDivisors _)⟩ + refine ⟨?_, ramificationIdx'_eq_one_of_map_localization hpP hp (primeCompl_le_nonZeroDivisors _)⟩ let Sₚ := Localization.AtPrime P - rw [← not_ne_iff (b := 1), ramificationIdx_ne_one_iff hpP, pow_two] + rw [← not_ne_iff (b := 1), ramificationIdx'_ne_one_iff hpP, pow_two] intro H₁ obtain ⟨a, ha⟩ : P ∣ p.map (algebraMap R S) := Ideal.dvd_iff_le.mpr hpP have ha' : ¬ a ≤ P := fun h ↦ H₁ (ha.trans_le (Ideal.mul_mono_right h)) @@ -275,12 +323,18 @@ lemma ramificationIdx_eq_one_iff rw [← not_ne_iff, IsLocalization.map_algebraMap_ne_top_iff_disjoint P.primeCompl] simpa [primeCompl, Set.disjoint_compl_left_iff_subset] -theorem ramificationIdx_le_ramificationIdx [IsDomain R] [IsTorsionFree R S] {S₀ : Type*} +@[deprecated (since := "2026-07-01")] alias ramificationIdx_eq_one_iff := + ramificationIdx'_eq_one_iff + +theorem ramificationIdx'_le_ramificationIdx' [IsDomain R] [IsTorsionFree R S] {S₀ : Type*} [CommRing S₀] [Algebra R S₀] [Algebra S₀ S] [IsScalarTower R S₀ S] (p : Ideal R) (P : Ideal S₀) (Q : Ideal S) [Q.LiesOver p] [hP : P.LiesOver p] [Q.IsPrime] (hp : p ≠ ⊥) : - Ideal.ramificationIdx P Q ≤ Ideal.ramificationIdx p Q := - p.ramificationIdx_le_ramificationIdx P Q ((liesOver_iff ..).mp hP) <| - ramificationIdx_ne_zero_of_liesOver _ hp + Ideal.ramificationIdx' P Q ≤ Ideal.ramificationIdx' p Q := + p.ramificationIdx'_le_ramificationIdx' P Q ((liesOver_iff ..).mp hP) <| + ramificationIdx'_ne_zero_of_liesOver _ hp + +@[deprecated (since := "2026-07-01")] alias ramificationIdx_le_ramificationIdx := + ramificationIdx'_le_ramificationIdx' theorem emultiplicity_map_eq_zero_of_ne [IsDedekindDomain R] {v : Ideal R} {w : Ideal S} {p : Ideal R} (hv : Irreducible v) (hp : Prime p) (hvp : v ≠ p) [w.LiesOver v] : @@ -289,17 +343,17 @@ theorem emultiplicity_map_eq_zero_of_ne [IsDedekindDomain R] {v : Ideal R} rw [Ideal.dvd_iff_le, Ideal.map_le_iff_le_comap, ← under_def, ← Ideal.over_def w v] at h exact ((isPrime_of_prime hp).isMaximal hp.ne_zero).eq_of_le (isPrime_of_prime hv.prime).ne_top h -/-- Use the more general result `emultiplicity_map_eq_ramificationIdx_mul`. +/-- Use the more general result `emultiplicity_map_eq_ramificationIdx'_mul`. This is a helper lemma. -/ -private theorem emultiplicity_map_eq_ramificationIdx_mul_of_prime [IsDedekindDomain R] +private theorem emultiplicity_map_eq_ramificationIdx'_mul_of_prime [IsDedekindDomain R] [FaithfulSMul R S] {v : Ideal R} {w : Ideal S} {p : Ideal R} (hv : Irreducible v) (hp : Prime p) (hw : Irreducible w) (hw_bot : w ≠ ⊥) [w.LiesOver v] : emultiplicity w (p.map (algebraMap R S)) = - v.ramificationIdx w * emultiplicity v p := by + v.ramificationIdx' w * emultiplicity v p := by have hp_bot : p.map (algebraMap R S) ≠ ⊥ := map_ne_bot_of_ne_bot hp.ne_zero by_cases hvp : v = p · simp [hvp, (FiniteMultiplicity.of_prime_left hp hp.ne_zero).emultiplicity_self, - ramificationIdx_eq_normalizedFactors_count hp_bot (isPrime_of_prime hw.prime) hw_bot, + ramificationIdx'_eq_normalizedFactors_count hp_bot (isPrime_of_prime hw.prime) hw_bot, emultiplicity_eq_count_normalizedFactors hw hp_bot] · rw [emultiplicity_eq_zero_of_irreducible_ne hv hp.irreducible hvp, mul_zero, emultiplicity_map_eq_zero_of_ne hv hp hvp] @@ -307,11 +361,11 @@ private theorem emultiplicity_map_eq_ramificationIdx_mul_of_prime [IsDedekindDom /-- If `v` is an irreducible ideal of `R`, `w` is an irreducible ideal of `S` lying over `v`, and `I` is an ideal of `R`, then the multiplicity of `w` in `I.map (algebraMap R S)` is given by the multiplicity of `v` in `I` multiplied by the ramification index of `w` over `v`. -/ -theorem emultiplicity_map_eq_ramificationIdx_mul [IsDedekindDomain R] +theorem emultiplicity_map_eq_ramificationIdx'_mul [IsDedekindDomain R] [FaithfulSMul R S] {v : Ideal R} {w : Ideal S} {I : Ideal R} (h : I ≠ ⊥) (hv : Irreducible v) (hw : Irreducible w) (hw_bot : w ≠ ⊥) [w.LiesOver v] : emultiplicity w (I.map (algebraMap R S)) = - v.ramificationIdx w * emultiplicity v I := by + v.ramificationIdx' w * emultiplicity v I := by induction I using induction_on_prime with | h₁ => aesop | h₂ I hI => @@ -321,7 +375,10 @@ theorem emultiplicity_map_eq_ramificationIdx_mul [IsDedekindDomain R] simp | h₃ I p hI hp IH => rw [Ideal.map_mul, emultiplicity_mul hw.prime, emultiplicity_mul hv.prime, IH hI, mul_add, - emultiplicity_map_eq_ramificationIdx_mul_of_prime hv hp hw hw_bot] + emultiplicity_map_eq_ramificationIdx'_mul_of_prime hv hp hw hw_bot] + +@[deprecated (since := "2026-07-01")] alias emultiplicity_map_eq_ramificationIdx_mul := + emultiplicity_map_eq_ramificationIdx'_mul end IsDedekindDomain @@ -334,12 +391,12 @@ variable [Algebra R S] [Algebra S T] [Algebra R T] [IsScalarTower R S T] /-- Let `T / S / R` be a tower of algebras, `p, P, Q` be ideals in `R, S, T` respectively, and `P` and `Q` are prime. If `P = Q ∩ S`, then `e (Q | p) = e (P | p) * e (Q | P)`. -/ -theorem ramificationIdx_algebra_tower [IsDedekindDomain S] [IsDedekindDomain T] +theorem ramificationIdx'_algebra_tower [IsDedekindDomain S] [IsDedekindDomain T] {p : Ideal R} {P : Ideal S} {Q : Ideal T} [hpm : P.IsPrime] [hqm : Q.IsPrime] (hg0 : map (algebraMap S T) P ≠ ⊥) (hfg : map (algebraMap R T) p ≠ ⊥) (hg : map (algebraMap S T) P ≤ Q) : - ramificationIdx p Q = - ramificationIdx p P * ramificationIdx P Q := by + ramificationIdx' p Q = + ramificationIdx' p P * ramificationIdx' P Q := by classical have hf0 : map (algebraMap R S) p ≠ ⊥ := by rw [IsScalarTower.algebraMap_eq R S T, ← map_map] at hfg @@ -347,9 +404,9 @@ theorem ramificationIdx_algebra_tower [IsDedekindDomain S] [IsDedekindDomain T] have hp0 : P ≠ ⊥ := ne_bot_of_map_ne_bot hg0 have hq0 : Q ≠ ⊥ := ne_bot_of_le_ne_bot hg0 hg letI : P.IsMaximal := Ring.DimensionLEOne.maximalOfPrime hp0 hpm - rw [IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count hf0 hpm hp0, - IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count hg0 hqm hq0, - IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count hfg hqm hq0, + rw [IsDedekindDomain.ramificationIdx'_eq_normalizedFactors_count hf0 hpm hp0, + IsDedekindDomain.ramificationIdx'_eq_normalizedFactors_count hg0 hqm hq0, + IsDedekindDomain.ramificationIdx'_eq_normalizedFactors_count hfg hqm hq0, IsScalarTower.algebraMap_eq R S T, ← map_map] rcases eq_prime_pow_mul_coprime hf0 P with ⟨I, hcp, heq⟩ have hcp : ⊤ = map (algebraMap S T) P ⊔ map (algebraMap S T) I := by rw [← map_sup, hcp, map_top] @@ -361,13 +418,14 @@ theorem ramificationIdx_algebra_tower [IsDedekindDomain S] [IsDedekindDomain T] exact add_eq_left.mpr <| Decidable.byContradiction fun h ↦ hntq <| hcp.trans_le <| sup_le hg <| le_of_dvd <| dvd_of_mem_normalizedFactors <| Multiset.count_ne_zero.mp h -@[deprecated (since := "2026-03-25")] alias ramificationIdx_tower := ramificationIdx_algebra_tower +@[deprecated (since := "2026-07-01")] alias ramificationIdx_algebra_tower := + ramificationIdx'_algebra_tower -theorem ramificationIdx_algebra_tower' [IsDedekindDomain S] [IsDedekindDomain T] [IsDomain R] +theorem ramificationIdx'_algebra_tower' [IsDedekindDomain S] [IsDedekindDomain T] [IsDomain R] [Module.IsTorsionFree R S] [Module.IsTorsionFree S T] (p : Ideal R) (P : Ideal S) (Q : Ideal T) [Q.IsPrime] [Q.LiesOver P] [P.LiesOver p] : - ramificationIdx p Q = - ramificationIdx p P * ramificationIdx P Q := by + ramificationIdx' p Q = + ramificationIdx' p P * ramificationIdx' P Q := by obtain rfl | hp := eq_or_ne p ⊥ · simp have : P.IsPrime := isPrime_of_liesOver Q P @@ -375,9 +433,12 @@ theorem ramificationIdx_algebra_tower' [IsDedekindDomain S] [IsDedekindDomain T] refine Module.IsTorsionFree.of_smul_eq_zero fun r m h ↦ ?_ rwa [algebra_compatible_smul S, smul_eq_zero, FaithfulSMul.algebraMap_eq_zero_iff] at h have hP : P ≠ ⊥ := ne_bot_of_liesOver_of_ne_bot hp _ - exact ramificationIdx_algebra_tower (map_ne_bot_of_ne_bot hP) (map_ne_bot_of_ne_bot hp) + exact ramificationIdx'_algebra_tower (map_ne_bot_of_ne_bot hP) (map_ne_bot_of_ne_bot hp) <| map_le_iff_le_comap.mpr <| le_of_eq <| over_def Q P +@[deprecated (since := "2026-07-01")] alias ramificationIdx_algebra_tower' := + ramificationIdx'_algebra_tower' + end tower end Ideal diff --git a/Mathlib/NumberTheory/RamificationInertia/Unramified.lean b/Mathlib/NumberTheory/RamificationInertia/Unramified.lean index ccac5888695ab6..c484d8c00d5481 100644 --- a/Mathlib/NumberTheory/RamificationInertia/Unramified.lean +++ b/Mathlib/NumberTheory/RamificationInertia/Unramified.lean @@ -5,10 +5,8 @@ Authors: Andrew Yang -/ module -public import Mathlib.NumberTheory.RamificationInertia.Basic -public import Mathlib.RingTheory.LocalRing.ResidueField.Instances -public import Mathlib.RingTheory.Unramified.LocalRing -public import Mathlib.LinearAlgebra.FreeModule.IdealQuotient +public import Mathlib.RingTheory.Ideal.Quotient.HasFiniteQuotients +public import Mathlib.RingTheory.RamificationInertia.Basic /-! @@ -29,18 +27,14 @@ variable {R S T : Type*} [CommRing R] [CommRing S] [CommRing T] variable [Algebra R S] [Algebra S T] [Algebra R T] [IsScalarTower R S T] local notation3 "e(" P "|" R ")" => - Ideal.ramificationIdx (Ideal.under R P) P + Ideal.ramificationIdx P R open IsLocalRing Algebra lemma Ideal.ramificationIdx_eq_one_of_isUnramifiedAt - {p : Ideal S} [p.IsPrime] [IsNoetherianRing S] [IsUnramifiedAt R p] - (hp : p ≠ ⊥) [IsDomain S] [EssFiniteType R S] : + {p : Ideal S} [p.IsPrime] [IsUnramifiedAt R p] [EssFiniteType R S] : e(p|R) = 1 := - let := Localization.AtPrime.algebraOfLiesOver (p.under R) p - (Ideal.ramificationIdx_eq_one_of_map_localization Ideal.map_comap_le hp - p.primeCompl_le_nonZeroDivisors - ((isUnramifiedAt_iff_map_eq R (p.under R) p).mp ‹_›).2) + p.ramificationIdx_eq_one R variable (R) in lemma IsUnramifiedAt.of_liesOver_of_ne_bot @@ -65,41 +59,112 @@ lemma IsUnramifiedAt.of_liesOver_of_ne_bot exact le_bot_iff.mp (Ideal.map_comap_le) rw [IsScalarTower.algebraMap_eq _ S, ← Ideal.map_map, this, Localization.AtPrime.map_eq_maximalIdeal] - rw [← Ideal.IsDedekindDomain.ramificationIdx_eq_one_iff hp Ideal.map_comap_le, - ← not_ne_iff, Ideal.ramificationIdx_ne_one_iff Ideal.map_comap_le] + rw [← Ideal.IsDedekindDomain.ramificationIdx'_eq_one_iff hp Ideal.map_comap_le, + ← not_ne_iff, Ideal.ramificationIdx'_ne_one_iff Ideal.map_comap_le] intro H - have := Ideal.ramificationIdx_eq_one_of_map_localization + have := Ideal.ramificationIdx'_eq_one_of_map_localization (hp₀ ▸ Ideal.map_comap_le) (hP₂ hp) hP₁ h₂ - rw [← not_ne_iff, Ideal.ramificationIdx_ne_one_iff (hp₀ ▸ Ideal.map_comap_le)] at this + rw [← not_ne_iff, Ideal.ramificationIdx'_ne_one_iff (hp₀ ▸ Ideal.map_comap_le)] at this replace H := Ideal.map_mono (f := algebraMap S T) H rw [Ideal.map_map, ← IsScalarTower.algebraMap_eq, Ideal.map_pow] at H refine this (H.trans (Ideal.pow_right_mono ?_ _)) exact Ideal.map_le_iff_le_comap.mpr Ideal.LiesOver.over.le +section IsUnramifiedIn + +namespace Algebra + variable (R) in /-- 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 +lemma IsUnramifiedAt.of_liesOver (p : Ideal S) (P : Ideal T) [P.LiesOver p] [p.IsPrime] [P.IsPrime] [IsUnramifiedAt R P] [EssFiniteType R S] [EssFiniteType R T] [IsDedekindDomain S] [IsDomain T] [Module.IsTorsionFree S T] : IsUnramifiedAt R p := IsUnramifiedAt.of_liesOver_of_ne_bot R p P P.primeCompl_le_nonZeroDivisors (Ideal.ne_bot_of_liesOver_of_ne_bot · P) + /-- Let `R` be a domain of characteristic 0, finite rank over `ℤ`, `S` be a Dedekind domain that is a finite `R`-algebra. Let `p` be a prime of `S`, then `p` is unramified iff `e(p) = 1`. -/ -lemma Algebra.isUnramifiedAt_iff_of_isDedekindDomain - {p : Ideal S} [p.IsPrime] [IsDedekindDomain S] [EssFiniteType R S] [IsDomain R] - [Module.Finite ℤ R] [CharZero R] [Algebra.IsIntegral R S] - (hp : p ≠ ⊥) : - Algebra.IsUnramifiedAt R p ↔ e(p|R) = 1 := by - let := Localization.AtPrime.algebraOfLiesOver (p.under R) p - rw [isUnramifiedAt_iff_map_eq R (p.under R) p, and_iff_right, - Ideal.IsDedekindDomain.ramificationIdx_eq_one_iff hp Ideal.map_comap_le] - have : Finite (R ⧸ p.under R) := - Ideal.finiteQuotientOfFreeOfNeBot _ (mt Ideal.eq_bot_of_comap_eq_bot hp) - have : Finite ((p.under R).ResidueField) := IsLocalization.finite _ - (nonZeroDivisors (R ⧸ p.under R)) - infer_instance +@[deprecated "Use `Ideal.ramificationIdx'_eq_one_iff` instead." (since := "2026-06-30")] +lemma isUnramifiedAt_iff_of_isDedekindDomain + {p : Ideal S} [p.IsPrime] [EssFiniteType R S] [IsDomain R] + [Module.Finite ℤ R] [CharZero R] [Algebra.IsIntegral R S] : + Algebra.IsUnramifiedAt R p ↔ e(p|R) = 1 := + Ideal.ramificationIdx'_eq_one_iff.symm + +/-- In characteristic zero the generic point is unramified: if `S` is a domain that is integral +over a characteristic-zero domain `R` and `R → S` is injective, then `S` is unramified at the zero +ideal. -/ +theorem isUnramifiedAt_bot [IsDomain R] [IsDomain S] [Module.IsTorsionFree R S] [CharZero R] + [Algebra.IsIntegral R S] : IsUnramifiedAt R (⊥ : Ideal S) := by + have : IsFractionRing S (Localization.AtPrime (⊥ : Ideal S)) := by + simpa [Ideal.primeCompl_bot] using Localization.isLocalization (M := (⊥ : Ideal S).primeCompl) + let : Field (Localization.AtPrime (⊥ : Ideal S)) := IsFractionRing.toField S + have : FaithfulSMul R (Localization.AtPrime (⊥ : Ideal S)) := by + rw [faithfulSMul_iff_algebraMap_injective, + IsScalarTower.algebraMap_eq R S (Localization.AtPrime ⊥)] + exact (IsFractionRing.injective S _).comp (FaithfulSMul.algebraMap_injective R S) + let := FractionRing.liftAlgebra R (Localization.AtPrime (⊥ : Ideal S)) + have : Algebra.IsAlgebraic (FractionRing R) (Localization.AtPrime ⊥) := + isAlgebraic_of_isFractionRing R S (FractionRing R) (Localization.AtPrime (⊥ : Ideal S)) + have : FormallyUnramified (FractionRing R) (Localization.AtPrime (⊥ : Ideal S)) := + FormallyUnramified.of_isSeparable _ _ + exact FormallyUnramified.comp R (FractionRing R) (Localization.AtPrime ⊥) + +/-- In characteristic zero, the zero ideal is unramified in an integral domain extension. -/ +theorem isUnramifiedIn_bot [IsDomain R] [IsDomain S] [FaithfulSMul R S] [CharZero R] + [Algebra.IsIntegral R S] : IsUnramifiedIn S (⊥ : Ideal R) := by + intro P _ hP + simpa [Ideal.eq_bot_of_liesOver_bot R P] using isUnramifiedAt_bot + +/-- Let `S` be a Dedekind domain that is torsion-free over a domain `R`, and let `p ≠ ⊥` be an +ideal of `R`. Then `p` is unramified in `S` if and only if `S` is unramified at every maximal +ideal `P` of `S` lying over `p`. + +See `Algebra.isUnramifiedIn_iff_forall_of_isDedekindDomain` if `R` is of characteristic zero. -/ +theorem isUnramifiedIn_iff_forall_of_isDedekindDomain' [IsDomain R] [IsDedekindDomain S] + [Module.IsTorsionFree R S] {p : Ideal R} (hp : p ≠ ⊥) : + IsUnramifiedIn S p ↔ + ∀ (P : Ideal S) (_ : P.IsMaximal), P.LiesOver p → IsUnramifiedAt R P := + ⟨fun h P hP hlo ↦ h P hP.isPrime hlo, + fun h P hP hlo ↦ h P (hP.isMaximal (Ideal.ne_bot_of_liesOver_of_ne_bot hp P)) hlo⟩ + +/-- Let `S` be a Dedekind domain that is integral and torsion-free over a characteristic-zero +domain `R`. Then an ideal `p` of `R` is unramified in `S` if and only if `S` is unramified at every +maximal ideal `P` of `S` lying over `p`. -/ +theorem isUnramifiedIn_iff_forall_of_isDedekindDomain [IsDomain R] [IsDedekindDomain S] + [Module.IsTorsionFree R S] [CharZero R] [Algebra.IsIntegral R S] {p : Ideal R} : + IsUnramifiedIn S p ↔ + ∀ (P : Ideal S) (_ : P.IsMaximal), P.LiesOver p → IsUnramifiedAt R P := by + refine ⟨fun h P hP hlo ↦ h P hP.isPrime hlo, fun h P hP hlo ↦ ?_⟩ + rcases eq_or_ne P ⊥ with rfl | hPbot + · exact isUnramifiedAt_bot + · exact h P (hP.isMaximal hPbot) hlo + +/-- For a prime `𝔓` of `S` lying over an unramified prime `𝔭` of `R`, the ramification index +`e(𝔓 ∣ 𝔭)` equals `1`. -/ +theorem IsUnramifiedIn.ramificationIdx_eq_one [IsDomain R] + [Module.Finite ℤ R] [CharZero R] [EssFiniteType R S] + [Algebra.IsIntegral R S] {𝔭 : Ideal R} (hunr : IsUnramifiedIn S 𝔭) {𝔓 : Ideal S} + [𝔓.IsPrime] (hP : 𝔓.LiesOver 𝔭) : Ideal.ramificationIdx 𝔓 R = 1 := + Ideal.ramificationIdx_eq_one_iff.mpr + (hunr 𝔓 inferInstance hP) + +/-- A nonzero ideal of `R` is unramified in `S` if and only if every prime ideal of `S` lying +over it has ramification index `1`. -/ +theorem isUnramifiedIn_iff_forall_ramificationIdx_eq_one [IsDomain R] + [Module.Finite ℤ R] [CharZero R] [EssFiniteType R S] + [Algebra.IsIntegral R S] {𝔭 : Ideal R} : + IsUnramifiedIn S 𝔭 ↔ + ∀ (𝔓 : Ideal S) [𝔓.IsPrime], 𝔓.LiesOver 𝔭 → Ideal.ramificationIdx 𝔓 R = 1 := by + refine ⟨fun hunr 𝔓 _ hP ↦ hunr.ramificationIdx_eq_one hP, fun h 𝔓 _ hP ↦ ?_⟩ + rw [← Ideal.ramificationIdx_eq_one_iff] + exact h 𝔓 hP + +end Algebra + +end IsUnramifiedIn diff --git a/Mathlib/NumberTheory/RamificationInertia/Valuation.lean b/Mathlib/NumberTheory/RamificationInertia/Valuation.lean index 70e22aa5a5bef1..8af1bd6a3711a9 100644 --- a/Mathlib/NumberTheory/RamificationInertia/Valuation.lean +++ b/Mathlib/NumberTheory/RamificationInertia/Valuation.lean @@ -42,19 +42,20 @@ variable [Algebra B L] [IsFractionRing B L] [IsScalarTower A B L] variable (v : HeightOneSpectrum A) (w : HeightOneSpectrum B) [w.asIdeal.LiesOver v.asIdeal] theorem intValuation_liesOver (x : A) : - v.intValuation x ^ (v.asIdeal.ramificationIdx w.asIdeal) = + v.intValuation x ^ (v.asIdeal.ramificationIdx' w.asIdeal) = w.intValuation (algebraMap A B x) := by - rcases eq_or_ne x 0 with rfl | hx; · simp [ramificationIdx_ne_zero_of_liesOver w.asIdeal v.ne_bot] + rcases eq_or_ne x 0 with rfl | hx + · simp [ramificationIdx'_ne_zero_of_liesOver w.asIdeal v.ne_bot] rw [intValuation_eq_exp_neg_multiplicity v hx, intValuation_eq_exp_neg_multiplicity w (by simpa), ← Set.image_singleton, ← Ideal.map_span, exp_neg, exp_neg, inv_pow, ← exp_nsmul, Int.nsmul_eq_mul, inv_inj, exp_inj, ← Nat.cast_mul, Nat.cast_inj] refine multiplicity_eq_of_emultiplicity_eq_some ?_ |>.symm replace hx : Ideal.span {x} ≠ ⊥ := by simp [hx] - rw [emultiplicity_map_eq_ramificationIdx_mul hx v.irreducible w.irreducible w.ne_bot, + rw [emultiplicity_map_eq_ramificationIdx'_mul hx v.irreducible w.irreducible w.ne_bot, Nat.cast_mul, (FiniteMultiplicity.of_prime_left v.prime hx).emultiplicity_eq_multiplicity] theorem valuation_liesOver (x : K) : - v.valuation K x ^ v.asIdeal.ramificationIdx w.asIdeal = + v.valuation K x ^ v.asIdeal.ramificationIdx' w.asIdeal = w.valuation L (algebraMap K L x) := by obtain ⟨x, y, hy, rfl⟩ := IsFractionRing.div_surjective (A := A) x simp [valuation_of_algebraMap, div_pow, ← IsScalarTower.algebraMap_apply A K L, @@ -81,7 +82,7 @@ theorem uniformContinuous_algebraMap_liesOver : v v `γL : ValuativeRel.ValueGroupWithZero Lʷ` `γK: ValuativeRel.ValueGroupWithZero Kᵛ` -/ - let e := v.asIdeal.ramificationIdx w.asIdeal + let e := v.asIdeal.ramificationIdx' w.asIdeal -- push `γL` to `ℤᵐ⁰` let σL := WithVal.valueGroupOrderIso₀ (w.valuation L) let σw := valueGroup₀_equiv_withZeroMulInt (w.valuation L) @@ -110,7 +111,7 @@ theorem uniformContinuous_algebraMap_liesOver : ← log_lt_log (by simp_all) (by simp [EmbeddingLike.map_eq_zero_iff (f := σwV)]), log_pow, nsmul_eq_mul, mul_comm] exact Int.mul_lt_of_lt_ediv - (mod_cast pos_of_ne_zero (ramificationIdx_ne_zero_of_liesOver w.asIdeal v.ne_bot)) hx + (mod_cast pos_of_ne_zero (ramificationIdx'_ne_zero_of_liesOver w.asIdeal v.ne_bot)) hx end AKLB diff --git a/Mathlib/NumberTheory/SelbergSieve.lean b/Mathlib/NumberTheory/SelbergSieve.lean index 93cfa77d4eb6ff..af6311cc74e0c1 100644 --- a/Mathlib/NumberTheory/SelbergSieve.lean +++ b/Mathlib/NumberTheory/SelbergSieve.lean @@ -23,8 +23,8 @@ minor notational difference is that we write $\nu(n)$ in place of $\frac{\omega( ## Results * `siftedSum_le_mainSum_errSum_of_UpperBoundSieve` - Every upper bound sieve gives an upper bound on the size of the sifted set in terms of `mainSum` and `errSum` - * `upperMoebius_of_lambda_sq` - Lambda squared weights produce upper bound sieves - * `lambdaSquared_mainSum_eq_diag_quad_form` - The main sum of a Λ² sieve has a nice diagonalisation +* `upperMoebius_of_lambda_sq` - Lambda squared weights produce upper bound sieves +* `lambdaSquared_mainSum_eq_diag_quad_form` - The main sum of a Λ² sieve has a nice diagonalisation ## References @@ -89,10 +89,10 @@ open Lean Meta Qq /-- Extension for the `positivity` tactic: `BoundingSieve.weights`. -/ @[positivity BoundingSieve.weights _ _] -meta def evalBoundingSieveWeights : PositivityExt where eval {u α} _zα pα? e := do +meta def evalBoundingSieveWeights : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@BoundingSieve.weights $s $n) => - let some _ := pα? | pure .none assertInstancesCommute pure (.nonnegative q(BoundingSieve.weights_nonneg $s $n)) | _, _, _ => throwError "not BoundingSieve.weights" diff --git a/Mathlib/NumberTheory/TsumDivsorsAntidiagonal.lean b/Mathlib/NumberTheory/TsumDivsorsAntidiagonal.lean deleted file mode 100644 index f6faf763281802..00000000000000 --- a/Mathlib/NumberTheory/TsumDivsorsAntidiagonal.lean +++ /dev/null @@ -1,10 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.Algebra.Order.Field.Power -public import Mathlib.Analysis.Normed.Group.Basic -public import Mathlib.Data.EReal.Inv -public import Mathlib.NumberTheory.ArithmeticFunction.Misc -public import Mathlib.Topology.Algebra.InfiniteSum.Order -public import Mathlib.Topology.MetricSpace.Bounded - -deprecated_module (since := "2025-12-19") diff --git a/Mathlib/Order/Atoms.lean b/Mathlib/Order/Atoms.lean index 2eaea790ae9e8e..b91cbff332c5ee 100644 --- a/Mathlib/Order/Atoms.lean +++ b/Mathlib/Order/Atoms.lean @@ -115,6 +115,9 @@ lemma IsAtom.ne_bot_iff_eq (ha : IsAtom a) (hba : b ≤ a) : b ≠ ⊥ ↔ b = a theorem IsAtom.Iic_eq (h : IsAtom a) : Set.Iic a = {⊥, a} := Set.ext fun _ => h.le_iff +lemma Set.Iio_eq_singleton_bot_iff : Iio a = {⊥} ↔ IsAtom a := by + simp [IsAtom, superset_antisymm_iff, bot_lt_iff_ne_bot] + @[simp] theorem bot_covBy_iff : ⊥ ⋖ a ↔ IsAtom a := by simp only [CovBy, bot_lt_iff_ne_bot, IsAtom, not_imp_not] @@ -209,6 +212,9 @@ lemma IsCoatom.ne_top_iff_eq (ha : IsCoatom a) (hab : a ≤ b) : b ≠ ⊤ ↔ b theorem IsCoatom.Ici_eq (h : IsCoatom a) : Set.Ici a = {⊤, a} := h.dual.Iic_eq +lemma Set.Ioi_eq_singleton_top_iff : Ioi a = {⊤} ↔ IsCoatom a := by + simp [IsCoatom, superset_antisymm_iff, lt_top_iff_ne_top] + @[simp] theorem covBy_top_iff : a ⋖ ⊤ ↔ IsCoatom a := toDual_covBy_toDual_iff.symm.trans bot_covBy_iff diff --git a/Mathlib/Order/Basic.lean b/Mathlib/Order/Basic.lean index d03d6ac2f44b9d..cff09b9564d8aa 100644 --- a/Mathlib/Order/Basic.lean +++ b/Mathlib/Order/Basic.lean @@ -7,6 +7,7 @@ module public import Mathlib.Data.Subtype public import Mathlib.Order.Defs.LinearOrder +public import Mathlib.Order.Defs.Prop public import Mathlib.Order.Notation public import Mathlib.Tactic.Spread public import Mathlib.Tactic.Convert @@ -188,6 +189,8 @@ theorem ge_imp_ge_of_le_of_le (h₁ : a ≤ c) (h₂ : d ≤ b) : a ≥ b → c theorem gt_imp_gt_of_le_of_le (h₁ : a ≤ c) (h₂ : d ≤ b) : a > b → c > d := fun hab ↦ (h₂.trans_lt hab).trans_le h₁ +attribute [gcongr strict] lt_of_lt_of_le lt_of_lt_of_le' + namespace Mathlib.Tactic.GCongr open Lean Meta @@ -549,15 +552,6 @@ instance Ne.instIsEquiv_compl : IsEquiv α (· ≠ ·)ᶜ := by /-! ### Order instances on the function space -/ - -instance Pi.hasLe [∀ i, LE (π i)] : - LE (∀ i, π i) where le x y := ∀ i, x i ≤ y i - -@[to_dual self] -theorem Pi.le_def [∀ i, LE (π i)] {x y : ∀ i, π i} : - x ≤ y ↔ ∀ i, x i ≤ y i := - Iff.rfl - instance Pi.preorder [∀ i, Preorder (π i)] : Preorder (∀ i, π i) where __ := (inferInstance : LE (∀ i, π i)) le_refl := fun a i ↦ le_refl (a i) @@ -1098,14 +1092,6 @@ end PUnit section «Prop» -/-- Propositions form a complete Boolean algebra, where the `≤` relation is given by implication. -/ -instance Prop.le : LE Prop := - ⟨(· → ·)⟩ - -@[simp] -theorem le_Prop_eq : ((· ≤ ·) : Prop → Prop → Prop) = (· → ·) := - rfl - theorem subrelation_iff_le {r s : α → α → Prop} : Subrelation r s ↔ r ≤ s := Iff.rfl diff --git a/Mathlib/Order/Bounded.lean b/Mathlib/Order/Bounded.lean index f051832f17f380..53310a06b47475 100644 --- a/Mathlib/Order/Bounded.lean +++ b/Mathlib/Order/Bounded.lean @@ -11,6 +11,7 @@ public import Mathlib.Order.Bounds.Defs /-! # Bounded and unbounded sets + We prove miscellaneous lemmas about bounded and unbounded sets. Many of these are just variations on the same ideas, or similar results with a few minor differences. The file is divided into these different general ideas. diff --git a/Mathlib/Order/Bounds/Basic.lean b/Mathlib/Order/Bounds/Basic.lean index bdd91821551caa..8931a089fb9421 100644 --- a/Mathlib/Order/Bounds/Basic.lean +++ b/Mathlib/Order/Bounds/Basic.lean @@ -132,6 +132,10 @@ lemma DirectedOn.isCofinalFor_fst_image_prod_snd_image {β : Type*} [Preorder β obtain ⟨z, hz, hxz, hyz⟩ := hs _ hx _ hy exact ⟨z, hz, hxz.1, hyz.2⟩ +@[to_dual] +lemma IsCofinalFor.nonempty (h : IsCofinalFor s t) (hs : s.Nonempty) : t.Nonempty := + let ⟨_, ha⟩ := hs; let ⟨b, hb, _⟩ := h ha; ⟨b, hb⟩ + theorem IsCofinalFor.union_left (hc : IsCofinalFor s t) : IsCofinalFor (s ∪ t) t := by rintro a (has | hat) · exact hc has @@ -226,6 +230,13 @@ theorem IsLUB.of_subset_of_superset {s t p : Set α} (hs : IsLUB s a) (hp : IsLU (htp : t ⊆ p) : IsLUB t a := ⟨upperBounds_mono_set htp hp.1, lowerBounds_mono_set (upperBounds_mono_set hst) hs.2⟩ +/-- The least upper bound of a set is also the least upper bound of any cofinal subset. -/ +@[to_dual /-- The greatest lower bound of a set is also the greatest lower bound of any +coinitial subset. -/] +theorem IsLUB.of_isCofinalFor {s t : Set α} (hs : IsLUB s a) (hts : t ⊆ s) + (hst : IsCofinalFor s t) : IsLUB t a := + ⟨upperBounds_mono_set hts hs.1, fun _b hb ↦ hs.2 (upperBounds_mono_of_isCofinalFor hst hb)⟩ + @[to_dual] theorem IsLeast.mono (ha : IsLeast s a) (hb : IsLeast t b) (hst : s ⊆ t) : b ≤ a := hb.2 (hst ha.1) diff --git a/Mathlib/Order/Cofinal.lean b/Mathlib/Order/Cofinal.lean index ce94954545c4d6..1269f4bdf408f6 100644 --- a/Mathlib/Order/Cofinal.lean +++ b/Mathlib/Order/Cofinal.lean @@ -40,6 +40,10 @@ theorem isCofinal_empty_iff : IsCofinal (∅ : Set α) ↔ IsEmpty α := by refine ⟨fun h ↦ ⟨fun a ↦ ?_⟩, fun h ↦ .of_isEmpty⟩ simpa using h a +theorem IsCofinal.nonempty [Nonempty α] {s : Set α} (hs : IsCofinal s) : s.Nonempty := by + inhabit α + exact (hs default).imp fun _ ↦ And.left + @[simp] theorem isCofinal_singleton_iff {x : α} : IsCofinal {x} ↔ IsTop x := by simp [IsCofinal, IsTop] @@ -169,4 +173,8 @@ theorem isCofinal_setOf_imp_lt (r : α → α → Prop) [h : IsWellFounded α r] by_contra! hc' exact hb' c (hb.trans hc') hc +theorem isCofinal_range_of_strictMono [WellFoundedLT α] {f : α → α} (hf : StrictMono f) : + IsCofinal (range f) := + fun x ↦ ⟨_, ⟨x, rfl⟩, hf.le_apply⟩ + end LinearOrder diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean b/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean index 31ad01f242423e..70cd9673351c01 100644 --- a/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean +++ b/Mathlib/Order/ConditionallyCompleteLattice/Basic.lean @@ -742,7 +742,11 @@ end WithTop namespace Monotone -variable [Preorder α] [ConditionallyCompleteLattice β] {f : α → β} (h_mono : Monotone f) +variable [ConditionallyCompleteLattice β] + +section Preorder + +variable [Preorder α] {f : α → β} (h_mono : Monotone f) include h_mono /-! A monotone function into a conditionally complete lattice preserves the ordering properties of @@ -758,6 +762,24 @@ theorem csSup_image_le {s : Set α} (hs : s.Nonempty) {B : α} (hB : B ∈ upper sSup (f '' s) ≤ f B := csSup_le (Nonempty.image f hs) (h_mono.mem_upperBounds_image hB) +end Preorder + +section ConditionallyCompleteLattice + +variable [ConditionallyCompleteLattice α] +variable {f : α → β} {s : Set α} (hs : s.Nonempty) (hf : Monotone f) +include hs hf + +theorem csSup_image_le_map_csSup (hbdd : BddAbove s := by bddDefault) : + sSup (f '' s) ≤ f (sSup s) := + csSup_image_le hf hs <| isLUB_csSup hs hbdd |>.left + +theorem map_csInf_le_csInf_image (hbdd : BddBelow s := by bddDefault) : + f (sInf s) ≤ sInf (f '' s) := + le_csInf_image hf hs <| isGLB_csInf hs hbdd |>.left + +end ConditionallyCompleteLattice + end Monotone lemma MonotoneOn.csInf_eq_of_subset_of_forall_exists_le diff --git a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean index 386e4e5f5ff243..13265fb034c9c9 100644 --- a/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean +++ b/Mathlib/Order/ConditionallyCompleteLattice/Finset.lean @@ -51,6 +51,23 @@ theorem Set.Finite.csSup_lt_iff (hs : s.Finite) (h : s.Nonempty) : sSup s < a theorem Set.Finite.lt_csInf_iff (hs : s.Finite) (h : s.Nonempty) : a < sInf s ↔ ∀ x ∈ s, a < x := @Set.Finite.csSup_lt_iff αᵒᵈ _ _ _ hs h +section ConditionallyCompleteLattice + +variable [ConditionallyCompleteLattice β] {f : α → β} (hmono : Monotone f) +include hmono + +theorem Set.Finite.map_sSup_of_monotone {s : Set α} (hne : s.Nonempty) (hfin : s.Finite) : + f (sSup s) = sSup (f '' s) := + le_antisymm (hmono.le_csSup_image (hne.csSup_mem hfin) hfin.bddAbove) + (hmono.csSup_image_le_map_csSup hne hfin.bddAbove) + +theorem Set.Finite.map_sInf_of_monotone {s : Set α} (hne : s.Nonempty) (hfin : s.Finite) : + f (sInf s) = sInf (f '' s) := + le_antisymm (hmono.map_csInf_le_csInf_image hne hfin.bddBelow) + (hmono.csInf_image_le (hne.csInf_mem hfin) hfin.bddBelow) + +end ConditionallyCompleteLattice + variable (f : ι → α) theorem Finset.ciSup_eq_max'_image {s : Finset ι} (h : ∃ x ∈ s, sSup ∅ ≤ f x) diff --git a/Mathlib/Order/Cover.lean b/Mathlib/Order/Cover.lean index 869ec148622016..ba26438da4ee20 100644 --- a/Mathlib/Order/Cover.lean +++ b/Mathlib/Order/Cover.lean @@ -384,6 +384,19 @@ theorem CovBy.Ico_eq (h : a ⋖ b) : Ico a b = {a} := by theorem CovBy.Icc_eq (h : a ⋖ b) : Icc a b = {a, b} := h.wcovBy.Icc_eq +@[to_dual] +theorem Set.Ico_eq_singleton_iff : Ico a b = {c} ↔ a = c ∧ a ⋖ b where + mp h := by + simp_rw [Set.ext_iff, mem_Ico, mem_singleton_iff] at h + have ⟨hac, hcb⟩ := (h c).mpr rfl + obtain rfl := (h a).mp ⟨le_refl a, hac.trans_lt hcb⟩ + exact ⟨rfl, ⟨hcb, fun d hcd hdb ↦ hcd.ne ((h d).mp ⟨hcd.le, hdb⟩).symm⟩⟩ + mpr := fun ⟨rfl, hcov⟩ ↦ hcov.Ico_eq + +@[to_dual Ioc_eq_singleton_right_iff] +lemma Set.Ico_eq_singleton_left_iff : Ico a b = {a} ↔ a ⋖ b := by + simp [Ico_eq_singleton_iff] + end PartialOrder section LinearOrder @@ -409,6 +422,26 @@ theorem CovBy.Iio_eq (h : a ⋖ b) : Iio b = Iic a := by theorem CovBy.Ioo_eq_Ico (h : a ⋖ b) (c : α) : Ioo a c = Ico b c := subset_antisymm (fun _x hx ↦ ⟨h.ge_of_gt hx.1, hx.2⟩) <| Ico_subset_Ioo_left h.lt +@[to_dual none] +theorem Set.Ioo_eq_singleton_iff : Ioo a b = {c} ↔ a ⋖ c ∧ c ⋖ b where + mp h := by + simp_rw [Set.ext_iff, mem_Ioo, mem_singleton_iff] at h + have ⟨hac, hcb⟩ := (h c).mpr rfl + exact ⟨⟨hac, fun d had hdc ↦ hdc.ne ((h d).mp ⟨had, hdc.trans hcb⟩)⟩, + ⟨hcb, fun d hcd hdb ↦ hcd.ne ((h d).mp ⟨hac.trans hcd, hdb⟩).symm⟩⟩ + mpr := fun ⟨hac, hcb⟩ ↦ by + rw [← Ioc_union_Ico_eq_Ioo hac.lt hcb.lt, hac.Ioc_eq, hcb.Ico_eq, union_self] + +@[to_dual] +theorem Set.Ioi_eq_singleton_iff : Ioi a = {b} ↔ IsTop b ∧ a ⋖ b where + mp h := by + simp_rw [Set.ext_iff, mem_Ioi, mem_singleton_iff] at h + have hb : a < b := (h b).mpr rfl + exact ⟨fun c ↦ not_lt.mp fun hc ↦ hc.ne.symm ((h c).mp (hb.trans hc)), + ⟨hb, fun c hac hcb ↦ hcb.ne ((h c).mp hac)⟩⟩ + mpr := fun ⟨hb, hab⟩ ↦ by + cases b, hb using IsTop.rec; rwa [← Ioc_top, Ioc_eq_singleton_right_iff] + @[to_dual unique_right] theorem CovBy.unique_left (ha : a ⋖ c) (hb : b ⋖ c) : a = b := (hb.le_of_lt ha.lt).antisymm <| ha.le_of_lt hb.lt diff --git a/Mathlib/Order/Defs/Prop.lean b/Mathlib/Order/Defs/Prop.lean new file mode 100644 index 00000000000000..2491ab8634ff70 --- /dev/null +++ b/Mathlib/Order/Defs/Prop.lean @@ -0,0 +1,32 @@ +/- +Copyright (c) 2016 Johannes Hölzl. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Johannes Hölzl, Yury Kudryashov +-/ +module + +import Mathlib.Tactic.ToDual + +/-! +# Order definitions for propositions + +This file defines orders on `Pi` and `Prop`. +-/ + +public section + +instance Pi.hasLe {ι : Type*} {π : ι → Type*} [∀ i, LE (π i)] : LE (∀ i, π i) where + le x y := ∀ i, x i ≤ y i + +@[to_dual self] +theorem Pi.le_def {ι : Type*} {π : ι → Type*} [∀ i, LE (π i)] {x y : ∀ i, π i} : + x ≤ y ↔ ∀ i, x i ≤ y i := + .rfl + +/-- Propositions form a complete Boolean algebra, where the `≤` relation is given by implication. -/ +instance Prop.le : LE Prop := + ⟨(· → ·)⟩ + +@[simp] +theorem le_Prop_eq : ((· ≤ ·) : Prop → Prop → Prop) = (· → ·) := + rfl diff --git a/Mathlib/Order/DirSupClosed.lean b/Mathlib/Order/DirSupClosed.lean index ead09bf1ac225a..2ea2a0708286fc 100644 --- a/Mathlib/Order/DirSupClosed.lean +++ b/Mathlib/Order/DirSupClosed.lean @@ -171,32 +171,14 @@ theorem DirSupClosedOn.union (hDL : IsLowerSet D) (hs : DirSupClosedOn D s) (ht : DirSupClosedOn D t) : DirSupClosedOn D (s ∪ t) := by intro d hD hdu hd₀ hd₁ a ha have hdst : d ∩ s ∪ d ∩ t = d := by grind - rw [← hdst] at hd₀ hd₁ - wlog h : DirectedOn (· ≤ ·) (d ∩ s) ∧ (d ∩ s).Nonempty - · rw [union_comm] at hdu hd₀ hd₁ hdst ⊢ + wlog h : DirectedOn (· ≤ ·) (d ∩ s) ∧ IsCofinalFor (d ∩ t) (d ∩ s) + · rw [union_comm] at hdu hdst ⊢ exact this hDL ht hs hD hdu hd₀ hd₁ ha hdst <| - (directedOn_or_directedOn_of_union' hd₀ hd₁).resolve_right h - obtain ⟨hds, hn⟩ := h - by_cases had : a ∈ lowerBounds (upperBounds (d ∩ s)) - · exact .inl <| hs (hDL inter_subset_left hD) inter_subset_right hn hds - ⟨fun b hb ↦ ha.1 hb.1, had⟩ - · simp only [lowerBounds, mem_setOf_eq, not_forall] at had - obtain ⟨b, hb, hb'⟩ := had - have key : {x ∈ d | ¬ x ≤ b} ⊆ d ∩ t := fun a ⟨had, hab⟩ ↦ - ⟨had, (hdu had).resolve_left fun has ↦ hab <| hb ⟨had, has⟩⟩ - obtain ⟨w, hw⟩ : {x ∈ d | ¬ x ≤ b}.Nonempty := by - contrapose! hb' - apply ha.2 - aesop - refine Or.inr <| ht (hDL inter_subset_left hD) (key.trans inter_subset_right) - ⟨w, hw⟩ (fun x hx y hy ↦ ?_) ?_ - · obtain ⟨z, hz, hz'⟩ := hd₁ _ (.inr (key hx)) _ (.inr (key hy)) - exact ⟨z, ⟨⟨hdst ▸ hz, mt hz'.1.trans hx.2⟩, hz'⟩⟩ - · refine ⟨fun x hx ↦ ha.1 hx.1, fun x hx ↦ ha.2 fun y hy ↦ ?_⟩ - by_cases hyb : y ≤ b - · obtain ⟨z, hz, hxz, hyz⟩ := hd₁ _ (hdst ▸ hy) _ (.inr (key hw)) - exact hxz.trans (hx ⟨hdst ▸ hz, fun hzb ↦ hw.2 (hyz.trans hzb)⟩) - exact hx ⟨hy, hyb⟩ + (directedOn_union_iff.mp (by rwa [hdst])).resolve_right h + obtain ⟨hds, hcof⟩ := h + have hcof' : IsCofinalFor d (d ∩ s) := hcof.union_right.mono_left hdst.ge + exact .inl <| hs (hDL inter_subset_left hD) inter_subset_right + (hcof'.nonempty hd₀) hds (ha.of_isCofinalFor inter_subset_left hcof') theorem DirSupInaccOn.inter (hDL : IsLowerSet D) (hs : DirSupInaccOn D s) (ht : DirSupInaccOn D t) : DirSupInaccOn D (s ∩ t) := by @@ -228,18 +210,11 @@ theorem dirSupInaccOn_iff_inter_subset (hDL : IsLowerSet D) : mpr := .of_inter_subset mp h t hD ht₀ ht₁ a ha has := by by_contra! H - have H : ∀ b : t, ∃ c, b.1 ≤ c ∧ c ∈ t ∧ c ∉ s := by simpa [not_subset, and_assoc] using H - choose f hf using H - have := ht₀.to_subtype - have hft : range f ⊆ t := by grind - apply (h (hDL hft hD) (range_nonempty f) _ _ has).ne_empty - · aesop - · intro a ha b hb - obtain ⟨c, hc, _, _⟩ := ht₁ _ (hft ha) _ (hft hb) - have := hf ⟨c, hc⟩ - grind - · exact ⟨upperBounds_mono_set hft ha.1, - fun b hb ↦ ha.2 fun c hc ↦ (hf ⟨c, hc⟩).1.trans (hb <| by simp)⟩ + have hcof : IsCofinalFor t (t \ s) := by grind [IsCofinalFor, not_subset] + obtain ⟨x, hx, hxs⟩ := h (hDL sdiff_subset hD) (hcof.nonempty ht₀) + (ht₁.of_isCofinalFor sdiff_subset hcof) + (ha.of_isCofinalFor sdiff_subset hcof) has + exact hx.2 hxs /-- The condition `(d ∩ s).Nonempty` in `DirSupInacc` can be replaced with the stronger `∃ b ∈ d, Ici b ∩ d ⊆ s`. -/ diff --git a/Mathlib/Order/Filter/Extr.lean b/Mathlib/Order/Filter/Extr.lean index 0c646f48feabca..f8337827d8f967 100644 --- a/Mathlib/Order/Filter/Extr.lean +++ b/Mathlib/Order/Filter/Extr.lean @@ -9,6 +9,7 @@ public import Mathlib.Order.Filter.Tendsto public import Mathlib.Order.ConditionallyCompleteLattice.Indexed public import Mathlib.Algebra.Order.Group.Defs public import Mathlib.Data.Finset.Lattice.Fold +public import Mathlib.Tactic.CrossRefAttribute /-! # Minimum and maximum w.r.t. a filter and on a set @@ -114,6 +115,7 @@ def IsMaxOn := IsMaxFilter f (𝓟 s) a /-- `IsExtrOn f s a` means `IsMinOn f s a` or `IsMaxOn f s a` -/ +@[wikidata Q845060] def IsExtrOn : Prop := IsExtrFilter f (𝓟 s) a diff --git a/Mathlib/Order/Filter/Germ/Basic.lean b/Mathlib/Order/Filter/Germ/Basic.lean index dd0aa8723d3e62..8e4bdba81b88d5 100644 --- a/Mathlib/Order/Filter/Germ/Basic.lean +++ b/Mathlib/Order/Filter/Germ/Basic.lean @@ -256,6 +256,7 @@ theorem _root_.Filter.Tendsto.congr_germ {f g : β → γ} {l : Filter α} {l' : (h : f =ᶠ[l'] g) {φ : α → β} (hφ : Tendsto φ l l') : (f ∘ φ : Germ l γ) = g ∘ φ := EventuallyEq.germ_eq (h.comp_tendsto hφ) +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias Filter.Tendsto.congr_germ := Filter.Tendsto.congr_germ lemma isConstant_comp_tendsto {lc : Filter γ} {g : γ → α} diff --git a/Mathlib/Order/Hom/Basic.lean b/Mathlib/Order/Hom/Basic.lean index 5714f402520d64..22ef5cfb8f190a 100644 --- a/Mathlib/Order/Hom/Basic.lean +++ b/Mathlib/Order/Hom/Basic.lean @@ -285,6 +285,14 @@ def id : α →o α := instance : Inhabited (α →o α) := ⟨id⟩ +variable (α β) in +/-- Order homomorphisms are equivalent to relation homomorphisms between `LE` relations. -/ +def equivRelHom : (α →o β) ≃ @RelHom α β (· ≤ ·) (· ≤ ·) where + toFun f := ⟨f, @f.monotone⟩ + invFun f := ⟨f, @f.map_rel⟩ + left_inv _ := rfl + right_inv _ := rfl + /-- The preorder structure of `α →o β` is pointwise inequality: `f ≤ g ↔ ∀ a, f a ≤ g a`. -/ instance : Preorder (α →o β) := @Preorder.lift (α →o β) (α → β) _ DFunLike.coe @@ -929,6 +937,52 @@ from `α` and `β` to themselves are order-isomorphic. -/ def conj {α β} [Preorder α] [Preorder β] (f : α ≃o β) : (α →o α) ≃ (β →o β) := arrowCongr f f +/-- Transport an `OrderEmbedding` across a pair of `OrderIso`s, by pre- and post-composition. + +This is `Equiv.embeddingCongr`/`RelIso.relEmbeddingCongr` for `OrderEmbedding`. -/ +abbrev orderEmbeddingCongr (f : α ≃o γ) (g : β ≃o δ) : (α ↪o β) ≃ (γ ↪o δ) := + RelIso.relEmbeddingCongr f g + +@[simp] +theorem orderEmbeddingCongr_apply (f : α ≃o γ) (g : β ≃o δ) (h : α ↪o β) : + orderEmbeddingCongr f g h = .trans (.trans f.symm h) g := + rfl + +@[simp] +theorem orderEmbeddingCongr_symm_apply (f : α ≃o γ) (g : β ≃o δ) (h : γ ↪o δ) : + (orderEmbeddingCongr f g).symm h = .trans (.trans f h) g.symm := + rfl + +/-- Transport an `OrderIso` across a pair of `OrderIso`s, by pre- and post-composition. + +This is `Equiv.equivCongr`/`RelIso.relIsoCongr` for `OrderIso`. -/ +abbrev orderIsoCongr (f : α ≃o γ) (g : β ≃o δ) : (α ≃o β) ≃ (γ ≃o δ) := + RelIso.relIsoCongr f g + +@[simp] +theorem orderIsoCongr_apply (f : α ≃o γ) (g : β ≃o δ) (h : α ≃o β) : + orderIsoCongr f g h = .trans (.trans f.symm h) g := + rfl + +@[simp] +theorem orderIsoCongr_symm_apply (f : α ≃o γ) (g : β ≃o δ) (h : γ ≃o δ) : + (orderIsoCongr f g).symm h = .trans (.trans f h) g.symm := + rfl + +/-- A surjective order embedding is an order isomorphism. -/ +@[simps!] +noncomputable def ofSurjective (f : α ↪o β) (hf : Function.Surjective f) : α ≃o β := + RelIso.ofSurjective f hf + +/-- Surjective order embeddings are equivalent to order isomorphisms. -/ +@[simps apply symm_apply] +noncomputable def equivEmbeddingSurjective : + α ≃o β ≃ { f : α ↪o β // Function.Surjective f } where + toFun f := ⟨f, f.surjective⟩ + invFun f := ofSurjective f f.prop + left_inv _ := by ext; rfl + right_inv _ := rfl + /-- `Prod.swap` as an `OrderIso`. -/ def prodComm : α × β ≃o β × α where toEquiv := Equiv.prodComm α β diff --git a/Mathlib/Order/Interval/Finset/Basic.lean b/Mathlib/Order/Interval/Finset/Basic.lean index 47635befe8c107..e8e2ff53069fcd 100644 --- a/Mathlib/Order/Interval/Finset/Basic.lean +++ b/Mathlib/Order/Interval/Finset/Basic.lean @@ -1143,21 +1143,21 @@ restricted to pairs satisfying `a ⩿ b`. -/ lemma monotone_iff_forall_wcovBy [Preorder α] [LocallyFiniteOrder α] [Preorder β] (f : α → β) : Monotone f ↔ ∀ a b : α, a ⩿ b → f a ≤ f b := by refine ⟨fun hf _ _ h ↦ hf h.le, fun h a b hab ↦ ?_⟩ - simpa [transGen_eq_self] using TransGen.lift f h <| le_iff_transGen_wcovBy.mp hab + simpa [transGen_eq_self] using TransGen.lift f h a b <| le_iff_transGen_wcovBy.mp hab /-- A function from a locally finite partial order is monotone if and only if it is monotone when restricted to pairs satisfying `a ⋖ b`. -/ lemma monotone_iff_forall_covBy [PartialOrder α] [LocallyFiniteOrder α] [Preorder β] (f : α → β) : Monotone f ↔ ∀ a b : α, a ⋖ b → f a ≤ f b := by refine ⟨fun hf _ _ h ↦ hf h.le, fun h a b hab ↦ ?_⟩ - simpa [reflTransGen_eq_self] using ReflTransGen.lift f h <| le_iff_reflTransGen_covBy.mp hab + simpa [reflTransGen_eq_self] using ReflTransGen.lift f h a b <| le_iff_reflTransGen_covBy.mp hab /-- A function from a locally finite preorder is strictly monotone if and only if it is strictly monotone when restricted to pairs satisfying `a ⋖ b`. -/ lemma strictMono_iff_forall_covBy [Preorder α] [LocallyFiniteOrder α] [Preorder β] (f : α → β) : StrictMono f ↔ ∀ a b : α, a ⋖ b → f a < f b := by refine ⟨fun hf _ _ h ↦ hf h.lt, fun h a b hab ↦ ?_⟩ - have := Relation.TransGen.lift f h (a := a) (b := b) + have := Relation.TransGen.lift f h a b rw [← lt_iff_transGen_covBy, transGen_eq_self] at this exact this hab diff --git a/Mathlib/Order/Interval/Finset/Defs.lean b/Mathlib/Order/Interval/Finset/Defs.lean index 8eb21b9f6320a4..a5bffe2d632411 100644 --- a/Mathlib/Order/Interval/Finset/Defs.lean +++ b/Mathlib/Order/Interval/Finset/Defs.lean @@ -389,6 +389,9 @@ theorem _root_.Fintype.card_Ioi (a : α) [Fintype (Set.Ioi a)] : Fintype.card (Set.Ioi a) = #(Ioi a) := Fintype.card_of_finset' _ fun _ ↦ by simp +@[to_additive (attr := simp)] +lemma Ici_one_eq_univ [One α] [IsBotOneClass α] [Fintype α] : Ici (1 : α) = univ := by ext; simp + end LocallyFiniteOrderTop section OrderTop diff --git a/Mathlib/Order/Interval/Set/Basic.lean b/Mathlib/Order/Interval/Set/Basic.lean index b986fff1612786..bc913bfeaa7fcf 100644 --- a/Mathlib/Order/Interval/Set/Basic.lean +++ b/Mathlib/Order/Interval/Set/Basic.lean @@ -266,7 +266,7 @@ theorem Iic_ssubset_Iic : Iic a ⊂ Iic b ↔ a < b where mpr h := (ssubset_iff_of_subset (Iic_subset_Iic.mpr h.le)).mpr ⟨b, self_mem_Iic, fun h' => h.not_ge h'⟩ -@[to_dual (attr := simp)] +@[to_dual (attr := simp, gcongr strict)] theorem Iic_subset_Iio : Iic a ⊆ Iio b ↔ a < b := ⟨fun h => h self_mem_Iic, fun h _ hx => lt_of_le_of_lt hx h⟩ @@ -324,7 +324,7 @@ theorem Icc_ssubset_Icc_left (h₂ : a₂ ≤ b₂) (ha : a₂ < a₁) (hb : b theorem Ico_subset_Ioo (ha : a₂ < a₁) (hb : b₁ ≤ b₂) : Ico a₁ b₁ ⊆ Ioo a₂ b₂ := fun _ hx ↦ ⟨ha.trans_le hx.1, hx.2.trans_le hb⟩ -@[to_dual] +@[to_dual (attr := gcongr strict)] theorem Ico_subset_Ioo_left (h : a₁ < a₂) : Ico a₂ b ⊆ Ioo a₁ b := Ico_subset_Ioo h le_rfl @@ -332,7 +332,7 @@ theorem Ico_subset_Ioo_left (h : a₁ < a₂) : Ico a₂ b ⊆ Ioo a₁ b := theorem Icc_subset_Ioc (ha : a₂ < a₁) (hb : b₁ ≤ b₂) : Icc a₁ b₁ ⊆ Ioc a₂ b₂ := fun _ hx ↦ ⟨ha.trans_le hx.1, hx.2.trans hb⟩ -@[to_dual] +@[to_dual (attr := gcongr strict)] theorem Icc_subset_Ioc_left (h : a₁ < a₂) : Icc a₂ b ⊆ Ioc a₁ b := Icc_subset_Ioc h le_rfl @@ -464,6 +464,9 @@ section matched_intervals end matched_intervals +@[to_additive (attr := simp)] +lemma Ici_one_eq_univ [One α] [IsBotOneClass α] : Ici (1 : α) = univ := by ext; simp + end Preorder section PartialOrder diff --git a/Mathlib/Order/Northcott.lean b/Mathlib/Order/Northcott.lean index db31e98d3efd98..15eb5362b13d27 100644 --- a/Mathlib/Order/Northcott.lean +++ b/Mathlib/Order/Northcott.lean @@ -5,7 +5,7 @@ Authors: Thomas Browning -/ module -public import Mathlib.Order.Filter.Cofinite +public import Mathlib.Order.Filter.TendstoCofinite /-! # Northcott Functions @@ -62,10 +62,11 @@ lemma comp_of_bddAbove [Preorder β] [LE γ] [Northcott h] (H : ∀ c, BddAbove exact (finite_le (h := h) b).subset <| by grind /-- A composition `h' ∘ h` is Northcott when `h'` is Northcott and the fibers of `h` are finite. -/ -lemma comp_of_finite_fibers [LE γ] [Northcott h'] (H : ∀ b, (h ⁻¹' {b}).Finite) : +lemma comp_of_finite_fibers [LE γ] [Northcott h'] [Filter.TendstoCofinite h] : Northcott (h' ∘ h) where finite_le c := by - refine Set.Finite.of_finite_fibers h ?_ fun x _ ↦ (H x).inter_of_right _ + refine Set.Finite.of_finite_fibers h ?_ fun x _ ↦ + (Filter.TendstoCofinite.finite_preimage_singleton h x).inter_of_right _ exact (finite_le (h := h') c).subset <| by grind end Northcott diff --git a/Mathlib/Order/OrdContinuous.lean b/Mathlib/Order/OrdContinuous.lean index e9ba9f618c3f10..2c7ab0a6024db4 100644 --- a/Mathlib/Order/OrdContinuous.lean +++ b/Mathlib/Order/OrdContinuous.lean @@ -34,15 +34,15 @@ open Function OrderDual Set -/ -/-- A function `f` between preorders is left order continuous if it preserves all suprema. We -define it using `IsLUB` instead of `sSup` so that the proof works both for complete lattices and -conditionally complete lattices. -/ +/-- A function `f` between preorders is left order continuous if it preserves all suprema of +nonempty sets. We define it using `IsLUB` instead of `sSup` so that the proof works both for +complete lattices and conditionally complete lattices. -/ @[to_dual -/-- A function `f` between preorders is right order continuous if it preserves all infima. We -define it using `IsGLB` instead of `sInf` so that the proof works both for complete lattices and -conditionally complete lattices. -/] +/-- A function `f` between preorders is right order continuous if it preserves all infima of +nonempty sets. We define it using `IsGLB` instead of `sInf` so that the proof works both for +complete lattices and conditionally complete lattices. -/] def LeftOrdContinuous [Preorder α] [Preorder β] (f : α → β) := - ∀ ⦃s : Set α⦄ ⦃x⦄, IsLUB s x → IsLUB (f '' s) (f x) + ∀ ⦃s : Set α⦄ ⦃x⦄, s.Nonempty → IsLUB s x → IsLUB (f '' s) (f x) namespace LeftOrdContinuous @@ -51,7 +51,7 @@ section Preorder variable (α) [Preorder α] [Preorder β] [Preorder γ] {g : β → γ} {f : α → β} @[to_dual] -protected theorem id : LeftOrdContinuous (id : α → α) := fun s x h => by +protected theorem id : LeftOrdContinuous (id : α → α) := fun s _ x h => by simpa only [image_id] using! h variable {α} @@ -69,7 +69,7 @@ protected theorem dual : @[to_dual] theorem map_isGreatest (hf : LeftOrdContinuous f) {s : Set α} {x : α} (h : IsGreatest s x) : IsGreatest (f '' s) (f x) := - ⟨mem_image_of_mem f h.1, (hf h.isLUB).1⟩ + ⟨mem_image_of_mem f h.1, (hf ⟨x, h.1⟩ h.isLUB).1⟩ @[to_dual] theorem mono (hf : LeftOrdContinuous f) : Monotone f := fun a₁ a₂ h => @@ -78,7 +78,7 @@ theorem mono (hf : LeftOrdContinuous f) : Monotone f := fun a₁ a₂ h => @[to_dual] theorem comp (hg : LeftOrdContinuous g) (hf : LeftOrdContinuous f) : LeftOrdContinuous (g ∘ f) := - fun s x h => by simpa only [image_image] using! hg (hf h) + fun s x hs h => by simpa only [image_image] using! hg (.image _ hs) (hf hs h) @[to_dual] protected theorem iterate {f : α → α} (hf : LeftOrdContinuous f) (n : ℕ) : @@ -95,7 +95,7 @@ variable [SemilatticeSup α] [SemilatticeSup β] {f : α → β} @[to_dual] theorem map_sup (hf : LeftOrdContinuous f) (x y : α) : f (x ⊔ y) = f x ⊔ f y := - (hf isLUB_pair).unique <| by simp only [image_pair, isLUB_pair] + (hf (insert_nonempty ..) isLUB_pair).unique <| by simp only [image_pair, isLUB_pair] @[to_dual] theorem le_iff (hf : LeftOrdContinuous f) (h : Injective f) {x y} : f x ≤ f y ↔ x ≤ y := by @@ -127,16 +127,19 @@ section CompleteLattice variable [CompleteLattice α] [CompleteLattice β] {f : α → β} @[to_dual] -theorem map_sSup' (hf : LeftOrdContinuous f) (s : Set α) : f (sSup s) = sSup (f '' s) := - (hf <| isLUB_sSup s).sSup_eq.symm +theorem map_sSup' (hf : LeftOrdContinuous f) {s : Set α} (hs : s.Nonempty) : + f (sSup s) = sSup (f '' s) := + (hf hs <| isLUB_sSup s).sSup_eq.symm @[to_dual] -theorem map_sSup (hf : LeftOrdContinuous f) (s : Set α) : f (sSup s) = ⨆ x ∈ s, f x := by - rw [hf.map_sSup', sSup_image] +theorem map_sSup (hf : LeftOrdContinuous f) {s : Set α} (hs : s.Nonempty) : + f (sSup s) = ⨆ x ∈ s, f x := by + rw [hf.map_sSup' hs, sSup_image] @[to_dual] -theorem map_iSup (hf : LeftOrdContinuous f) (g : ι → α) : f (⨆ i, g i) = ⨆ i, f (g i) := by - simp only [iSup, hf.map_sSup', ← range_comp] +theorem map_iSup (hf : LeftOrdContinuous f) [Nonempty ι] (g : ι → α) : + f (⨆ i, g i) = ⨆ i, f (g i) := by + simp only [iSup, hf.map_sSup' (range_nonempty g), ← range_comp] rfl end CompleteLattice @@ -148,7 +151,7 @@ variable [ConditionallyCompleteLattice α] [ConditionallyCompleteLattice β] [No @[to_dual] theorem map_csSup (hf : LeftOrdContinuous f) {s : Set α} (sne : s.Nonempty) (sbdd : BddAbove s) : f (sSup s) = sSup (f '' s) := - ((hf <| isLUB_csSup sne sbdd).csSup_eq <| sne.image f).symm + ((hf sne <| isLUB_csSup sne sbdd).csSup_eq <| sne.image f).symm @[to_dual] theorem map_ciSup (hf : LeftOrdContinuous f) {g : ι → α} (hg : BddAbove (range g)) : @@ -165,11 +168,11 @@ variable [Preorder α] [Preorder β] {f : α → β} {g : β → α} /-- A left adjoint in a Galois connection is left-continuous in the order-theoretic sense. -/ lemma leftOrdContinuous (gc : GaloisConnection f g) : LeftOrdContinuous f := - fun _ _ ↦ gc.isLUB_l_image + fun _ _ _ ↦ gc.isLUB_l_image /-- A right adjoint in a Galois connection is right-continuous in the order-theoretic sense. -/ lemma rightOrdContinuous (gc : GaloisConnection f g) : RightOrdContinuous g := - fun _ _ ↦ gc.isGLB_u_image + fun _ _ _ ↦ gc.isGLB_u_image end GaloisConnection diff --git a/Mathlib/Order/Partition/Finpartition.lean b/Mathlib/Order/Partition/Finpartition.lean index 7971be757c1bf9..5cd94e49929259 100644 --- a/Mathlib/Order/Partition/Finpartition.lean +++ b/Mathlib/Order/Partition/Finpartition.lean @@ -195,6 +195,24 @@ theorem ne_bot {b : α} (hb : b ∈ P.parts) : b ≠ ⊥ := by protected theorem disjoint : (P.parts : Set α).PairwiseDisjoint id := P.supIndep.pairwiseDisjoint +section Apply + +variable {β : Type*} {f : α → β} + +/-- The `sup` of a sup-bot-preserving map `f` over the parts of a `Finpartition` equals `f a`. -/ +theorem sup_parts_apply [SemilatticeSup β] [OrderBot β] (hf : ∀ x y, f (x ⊔ y) = f x ⊔ f y) + (hbot : f ⊥ = ⊥) : P.parts.sup f = f a := + (apply_sup_eq_sup_comp f hf hbot).symm.trans (congrArg f P.sup_parts) + +/-- Parts of a `Finpartition` are pairwise disjoint under an inf-bot-preserving map. -/ +theorem pairwiseDisjoint_apply [SemilatticeInf β] [OrderBot β] (hf : ∀ x y, f (x ⊓ y) = f x ⊓ f y) + (hbot : f ⊥ = ⊥) : (P.parts : Set α).PairwiseDisjoint f := by + intro _ hx _ hy hxy + have := (P.disjoint hx hy hxy).eq_bot + simp_all [disjoint_iff, ← hf] + +end Apply + variable {P} @[simp] diff --git a/Mathlib/Order/PiLex.lean b/Mathlib/Order/PiLex.lean index 11683b0a6d28d9..c4300651ca5b6d 100644 --- a/Mathlib/Order/PiLex.lean +++ b/Mathlib/Order/PiLex.lean @@ -81,11 +81,16 @@ theorem trichotomous_lex [∀ i, Std.Trichotomous (α := β i) s] (wf : WellFoun @[deprecated (since := "2026-01-24")] alias isTrichotomous_lex := trichotomous_lex +/- +These instances are leaky, because they define the relation on `∀ i, β i` instead of +`Lex (∀ i, β i)`/`Colex (∀ i, β i)`. So, we would like to mark them `@[semireducible]`. +But the linter doesn't allow this, so we wrap them in `id` instead. +-/ instance [LT ι] [∀ a, LT (β a)] : LT (Lex (∀ i, β i)) := - ⟨Pi.Lex (· < ·) (· < ·)⟩ + id ⟨Pi.Lex (· < ·) (· < ·)⟩ instance [LT ι] [∀ a, LT (β a)] : LT (Colex (∀ i, β i)) := - ⟨Pi.Lex (· > ·) (· < ·)⟩ + id ⟨Pi.Lex (· > ·) (· < ·)⟩ -- If `Lex` and `Colex` are ever made into one-field structures, we need a `CoeFun` instance. -- This will make `x i` syntactically equal to `ofLex x i` for `x : Πₗ i, α i`, thus making diff --git a/Mathlib/Order/Preorder/Chain.lean b/Mathlib/Order/Preorder/Chain.lean index 62045ef0de9174..ef314eaee043c2 100644 --- a/Mathlib/Order/Preorder/Chain.lean +++ b/Mathlib/Order/Preorder/Chain.lean @@ -119,6 +119,14 @@ theorem Monotone.isChain_range [LinearOrder α] [Preorder β] {f : α → β} (h rw [← image_univ] exact hf.isChain_image (isChain_of_trichotomous _) +lemma Antitone.isChain_image [Preorder α] [Preorder β] {s : Set α} {f : α → β} + (hf : Antitone f) (hs : IsChain (· ≤ ·) s) : IsChain (· ≤ ·) (f '' s) := + hf.dual_left.isChain_image hs.symm + +theorem Antitone.isChain_range [LinearOrder α] [Preorder β] {f : α → β} (hf : Antitone f) : + IsChain (· ≤ ·) (range f) := + hf.dual_left.isChain_range + theorem IsChain.lt_of_le [PartialOrder α] {s : Set α} (h : IsChain (· ≤ ·) s) : IsChain (· < ·) s := fun _a ha _b hb hne ↦ (h ha hb hne).imp hne.lt_of_le hne.lt_of_le' @@ -222,6 +230,15 @@ theorem IsChain.exists3 (hchain : IsChain r s) [IsTrans α r] {a b c} (mem1 : a end Total +/-- A chain in a partial order is a linear order. -/ +@[implicit_reducible] +def IsChain.linearOrder [PartialOrder α] [DecidableLE α] {s : Set α} (hs : IsChain (· ≤ ·) s) : + LinearOrder s where + le_total := by + rintro ⟨a, ha⟩ ⟨b, hb⟩ + exact hs.total ha hb + toDecidableLE x y := inferInstanceAs (Decidable (x.1 ≤ y.1)) + lemma IsChain.le_of_not_gt [Preorder α] (hs : IsChain (· ≤ ·) s) {x y : α} (hx : x ∈ s) (hy : y ∈ s) (h : ¬ x < y) : y ≤ x := by cases hs.total hx hy with diff --git a/Mathlib/Order/Preorder/Finite.lean b/Mathlib/Order/Preorder/Finite.lean index 24e16fe9ffe9e6..c64ac316b4bd62 100644 --- a/Mathlib/Order/Preorder/Finite.lean +++ b/Mathlib/Order/Preorder/Finite.lean @@ -23,6 +23,7 @@ namespace Finset section IsTrans variable [LE α] [IsTrans α LE.le] {s : Finset α} {a : α} +@[to_dual] lemma exists_maximalFor (f : ι → α) (s : Finset ι) (hs : s.Nonempty) : ∃ i, MaximalFor (· ∈ s) f i := by induction hs using Finset.Nonempty.cons_induction with @@ -35,26 +36,21 @@ lemma exists_maximalFor (f : ι → α) (s : Finset ι) (hs : s.Nonempty) : exact fun k hk hik ↦ _root_.trans (hj.2 hk <| _root_.trans hji hik) hji · exact ⟨j, mem_cons_of_mem hj.1, by simpa [hji] using hj.2⟩ -lemma exists_minimalFor (f : ι → α) (s : Finset ι) (hs : s.Nonempty) : - ∃ i, MinimalFor (· ∈ s) f i := exists_maximalFor (α := αᵒᵈ) f s hs - +@[to_dual] lemma exists_maximal (hs : s.Nonempty) : ∃ i, Maximal (· ∈ s) i := s.exists_maximalFor id hs -lemma exists_minimal (hs : s.Nonempty) : ∃ i, Minimal (· ∈ s) i := s.exists_minimalFor id hs end IsTrans section Preorder variable [Preorder α] {s : Finset α} {a : α} +@[to_dual] lemma exists_le_maximal (s : Finset α) (ha : a ∈ s) : ∃ b, a ≤ b ∧ Maximal (· ∈ s) b := by classical obtain ⟨b, hb, hab, hbmin⟩ : ∃ b ∈ s, a ≤ b ∧ _ := by simpa [Maximal, and_assoc] using {x ∈ s | a ≤ x}.exists_maximal ⟨a, mem_filter.2 ⟨ha, le_rfl⟩⟩ exact ⟨b, hab, hb, fun c hc hbc ↦ hbmin hc (hab.trans hbc) hbc⟩ -lemma exists_le_minimal (s : Finset α) (ha : a ∈ s) : ∃ b ≤ a, Minimal (· ∈ s) b := - exists_le_maximal (α := αᵒᵈ) s ha - end Preorder end Finset @@ -62,42 +58,33 @@ namespace Set section IsTrans variable [LE α] [IsTrans α LE.le] {s : Set α} {a : α} +@[to_dual] lemma Finite.exists_maximalFor (f : ι → α) (s : Set ι) (h : s.Finite) (hs : s.Nonempty) : ∃ i, MaximalFor (· ∈ s) f i := by lift s to Finset ι using h; exact s.exists_maximalFor f hs -lemma Finite.exists_minimalFor (f : ι → α) (s : Set ι) (h : s.Finite) (hs : s.Nonempty) : - ∃ i, MinimalFor (· ∈ s) f i := Finite.exists_maximalFor (α := αᵒᵈ) f s h hs - +@[to_dual] lemma Finite.exists_maximal (h : s.Finite) (hs : s.Nonempty) : ∃ i, Maximal (· ∈ s) i := h.exists_maximalFor id _ hs -lemma Finite.exists_minimal (h : s.Finite) (hs : s.Nonempty) : ∃ i, Minimal (· ∈ s) i := - h.exists_minimalFor id _ hs - /-- A version of `Finite.exists_maximalFor` with the (weaker) hypothesis that the image of `s` is finite rather than `s` itself. -/ +@[to_dual /- A version of `Finite.exists_minimalFor` with the (weaker) hypothesis that the image of +`s` is finite rather than `s` itself.-/] lemma Finite.exists_maximalFor' (f : ι → α) (s : Set ι) (h : (f '' s).Finite) (hs : s.Nonempty) : ∃ i, MaximalFor (· ∈ s) f i := by obtain ⟨_, ⟨a, ha, rfl⟩, hmax⟩ := Finite.exists_maximalFor id (f '' s) h (hs.image f) exact ⟨a, ha, fun a' ha' hf ↦ hmax (mem_image_of_mem f ha') hf⟩ -/-- A version of `Finite.exists_minimalFor` with the (weaker) hypothesis that the image of `s` -is finite rather than `s` itself. -/ -lemma Finite.exists_minimalFor' (f : ι → α) (s : Set ι) (h : (f '' s).Finite) (hs : s.Nonempty) : - ∃ i, MinimalFor (· ∈ s) f i := h.exists_maximalFor' (α := αᵒᵈ) f s hs - end IsTrans section Preorder variable [Preorder α] {s : Set α} {a : α} +@[to_dual] lemma Finite.exists_le_maximal (hs : s.Finite) (ha : a ∈ s) : ∃ b, a ≤ b ∧ Maximal (· ∈ s) b := by lift s to Finset α using hs; exact s.exists_le_maximal ha -lemma Finite.exists_le_minimal (hs : s.Finite) (ha : a ∈ s) : ∃ b, b ≤ a ∧ Minimal (· ∈ s) b := by - lift s to Finset α using hs; exact s.exists_le_minimal ha - variable [Nonempty α] lemma infinite_of_forall_exists_gt (h : ∀ a, ∃ b ∈ s, a < b) : s.Infinite := by @@ -107,6 +94,7 @@ lemma infinite_of_forall_exists_gt (h : ∀ a, ∃ b ∈ s, a < b) : s.Infinite exact infinite_of_injective_forall_mem (strictMono_nat_of_lt_succ fun n => (h _).choose_spec.2).injective hf +@[to_dual existing infinite_of_forall_exists_gt] lemma infinite_of_forall_exists_lt (h : ∀ a, ∃ b ∈ s, b < a) : s.Infinite := infinite_of_forall_exists_gt (α := αᵒᵈ) h @@ -115,8 +103,8 @@ end Preorder section PartialOrder variable (α) [PartialOrder α] +@[to_dual] lemma finite_isTop : {a : α | IsTop a}.Finite := (subsingleton_isTop α).finite -lemma finite_isBot : {a : α | IsBot a}.Finite := (subsingleton_isBot α).finite end PartialOrder @@ -152,10 +140,8 @@ end Set section Preorder variable [Preorder α] [Finite α] {p : α → Prop} {a : α} +@[to_dual] lemma Finite.exists_le_maximal (h : p a) : ∃ b, a ≤ b ∧ Maximal p b := {x | p x}.toFinite.exists_le_maximal h -lemma Finite.exists_le_minimal (h : p a) : ∃ b ≤ a, Minimal p b := - {x | p x}.toFinite.exists_le_minimal h - end Preorder diff --git a/Mathlib/Order/RelClasses.lean b/Mathlib/Order/RelClasses.lean index 63bc0a93b2ab8d..93485384b10b30 100644 --- a/Mathlib/Order/RelClasses.lean +++ b/Mathlib/Order/RelClasses.lean @@ -661,7 +661,6 @@ but after translation `instReflLe` becomes `instReflGe : Std.Refl (· ≥ ·)`. theorem Std.ge_refl {α : Type*} [LE α] [inst : @Std.Refl α (· ≥ ·)] (a : α) : a ≤ a := @Std.Refl.refl α (· ≥ ·) inst a -set_option linter.existingAttributeWarning false in attribute [to_dual existing Std.ge_refl] Std.le_refl @[to_dual instIsTransGe] diff --git a/Mathlib/Order/SemiconjSup.lean b/Mathlib/Order/SemiconjSup.lean index 9a8fca764974b4..e7ab5076672a2b 100644 --- a/Mathlib/Order/SemiconjSup.lean +++ b/Mathlib/Order/SemiconjSup.lean @@ -89,7 +89,7 @@ theorem Semiconj.symm_adjoint [PartialOrder α] [Preorder β] {fa : α ≃o α} Function.Semiconj g' fb fa := by refine fun y => (hg' _).unique ?_ rw [← fa.surjective.image_preimage { x | g x ≤ fb y }, preimage_setOf_eq] - simp only [h.eq, fb.le_iff_le, fa.leftOrdContinuous (hg' _)] + simp only [h.eq, fb.le_iff_le, fa.isLUB_image'.mpr (hg' _)] variable {G : Type*} @@ -97,7 +97,7 @@ theorem semiconj_of_isLUB [PartialOrder α] [Group G] (f₁ f₂ : G →* α ≃ (H : ∀ x, IsLUB (range fun g' => (f₁ g')⁻¹ (f₂ g' x)) (h x)) (g : G) : Function.Semiconj h (f₂ g) (f₁ g) := by refine fun y => (H _).unique ?_ - have := (f₁ g).leftOrdContinuous (H y) + have := (f₁ g).isLUB_image'.mpr (H y) rw [← range_comp, ← (Equiv.mulRight g).surjective.range_comp _] at this simpa [comp_def] using this diff --git a/Mathlib/Order/SuccPred/Basic.lean b/Mathlib/Order/SuccPred/Basic.lean index 6536f5edf298ea..b6947eeac6659e 100644 --- a/Mathlib/Order/SuccPred/Basic.lean +++ b/Mathlib/Order/SuccPred/Basic.lean @@ -232,9 +232,11 @@ theorem isMax_iterate_succ_of_eq_of_ne {n m : ℕ} (h_eq : succ^[n] a = succ^[m] · rw [h_eq] exact isMax_iterate_succ_of_eq_of_lt h_eq.symm (lt_of_le_of_ne h h_ne.symm) -@[to_dual] -theorem Iic_subset_Iio_succ_of_not_isMax (ha : ¬IsMax a) : Iic a ⊆ Iio (succ a) := - fun _ => (lt_succ_of_le_of_not_isMax · ha) +@[to_dual (attr := deprecated "use `gcongr`/`grw` and `lt_succ_of_not_isMax" + (since := "2026-06-06"))] +theorem Iic_subset_Iio_succ_of_not_isMax (ha : ¬IsMax a) : Iic a ⊆ Iio (succ a) := by + gcongr + exact lt_succ_of_not_isMax ha @[to_dual] theorem Ici_succ_of_not_isMax (ha : ¬IsMax a) : Ici (succ a) = Ioi a := @@ -242,15 +244,13 @@ theorem Ici_succ_of_not_isMax (ha : ¬IsMax a) : Ici (succ a) = Ioi a := @[to_dual Icc_subset_Ioc_pred_left_of_not_isMin] theorem Icc_subset_Ico_succ_right_of_not_isMax (hb : ¬IsMax b) : Icc a b ⊆ Ico a (succ b) := by - rw [← Ici_inter_Iio, ← Ici_inter_Iic] gcongr - exact Iic_subset_Iio_succ_of_not_isMax hb + exact lt_succ_of_not_isMax hb @[to_dual Ico_subset_Ioo_pred_left_of_not_isMin] theorem Ioc_subset_Ioo_succ_right_of_not_isMax (hb : ¬IsMax b) : Ioc a b ⊆ Ioo a (succ b) := by - rw [← Ioi_inter_Iio, ← Ioi_inter_Iic] gcongr - exact Iic_subset_Iio_succ_of_not_isMax hb + exact lt_succ_of_not_isMax hb @[to_dual Icc_pred_right_of_not_isMin] theorem Icc_succ_left_of_not_isMax (ha : ¬IsMax a) : Icc (succ a) b = Ioc a b := by diff --git a/Mathlib/Order/Types/Defs.lean b/Mathlib/Order/Types/Defs.lean index 82c08e974b1ee4..f0e7316273439b 100644 --- a/Mathlib/Order/Types/Defs.lean +++ b/Mathlib/Order/Types/Defs.lean @@ -270,10 +270,10 @@ theorem lift_type_eq_iff : lift (type α) = lift (type β) ↔ Nonempty (α ≃o exact ⟨(ULift.orderIso.symm.trans h.some).trans ULift.orderIso⟩ theorem lift_type_le_iff : lift (type α) ≤ lift (type β) ↔ Nonempty (α ↪o β) := by - refine ⟨fun h ↦ ?_, fun ⟨h⟩ ↦ type_le_type <| (ULift.orderIso.toOrderEmbedding.trans h).trans - ULift.orderIso.symm.toOrderEmbedding⟩ - rw [← type_ulift, ← type_ulift, type_le_type_iff] at h - exact ⟨(ULift.orderIso.symm.toOrderEmbedding.trans h.some).trans ULift.orderIso.toOrderEmbedding⟩ + refine ⟨fun h ↦ ?_, fun ⟨h⟩ ↦ type_le_type <| (ULift.orderIso.toOrderEmbedding.trans h).trans + ULift.orderIso.symm.toOrderEmbedding⟩ + rw [← type_ulift, ← type_ulift, type_le_type_iff] at h + exact ⟨(ULift.orderIso.symm.toOrderEmbedding.trans h.some).trans ULift.orderIso.toOrderEmbedding⟩ /-- `ω` is the first infinite order type, defined as the order type of `ℕ`. -/ @[expose] diff --git a/Mathlib/Order/UpperLower/CompleteLattice.lean b/Mathlib/Order/UpperLower/CompleteLattice.lean index f24575b9fefe00..5d6cd1de5de14d 100644 --- a/Mathlib/Order/UpperLower/CompleteLattice.lean +++ b/Mathlib/Order/UpperLower/CompleteLattice.lean @@ -337,7 +337,7 @@ def map (f : α ≃o β) : UpperSet α ≃o UpperSet β where @[to_dual (attr := simp)] theorem symm_map (f : α ≃o β) : (map f).symm = map f.symm := by - ext; simp [map, OrderIso.symm_apply_eq] + ext; simp [map, OrderIso.symm_apply_eq] @[to_dual (attr := simp)] theorem mem_map : b ∈ map f s ↔ f.symm b ∈ s := by diff --git a/Mathlib/Order/WellFounded.lean b/Mathlib/Order/WellFounded.lean index aa94d70693c099..b3bae400954623 100644 --- a/Mathlib/Order/WellFounded.lean +++ b/Mathlib/Order/WellFounded.lean @@ -95,6 +95,21 @@ theorem has_min {α} {r : α → α → Prop} (H : WellFounded r) (s : Set α) : not_imp_not.1 fun hne hx => hne <| ⟨x, hx, fun y hy hyx => hne <| IH y hyx hy⟩) ha +theorem not_rightTotal (wf : WellFounded r) [Nonempty α] : ¬ Relator.RightTotal r := by + intro h + obtain ⟨a, -, ha⟩ := wf.has_min Set.univ Set.univ_nonempty + obtain ⟨b, hba⟩ := h a + specialize ha b (Set.mem_univ b) + contradiction + +theorem not_leftTotal (wf : WellFounded (Function.swap r)) [Nonempty α] : + ¬ Relator.LeftTotal r := by + intro h + obtain ⟨a, -, ha⟩ := wf.has_min Set.univ Set.univ_nonempty + obtain ⟨b, hab⟩ := h a + specialize ha b (Set.mem_univ b) + contradiction + /-- A minimal element of a nonempty set in a well-founded order. If you're working with a nonempty linear order, consider defining a diff --git a/Mathlib/Probability/CentralLimitTheorem.lean b/Mathlib/Probability/CentralLimitTheorem.lean index 312e75db6e0570..1e1676806ae8f7 100644 --- a/Mathlib/Probability/CentralLimitTheorem.lean +++ b/Mathlib/Probability/CentralLimitTheorem.lean @@ -120,6 +120,7 @@ private theorem tendstoInDistribution_inv_sqrt_mul_var_mul_sum_sub independent, identically distributed with mean `μ` and variance `v`, and a random variable `Y : Ω' → ℝ` following `gaussianReal 0 v`, the sequence `n ↦ (√n)⁻¹ * (∑ k ∈ Finset.range n, X k ω - n * μ)` converges to `Y` in distribution. -/ +@[wikidata Q190391] theorem tendstoInDistribution_inv_sqrt_mul_sum_sub (hY : HasLaw Y (gaussianReal 0 Var[X 0; P].toNNReal) P') (hX : MemLp (X 0) 2 P) (hindep : iIndepFun X P) diff --git a/Mathlib/Probability/CondVar.lean b/Mathlib/Probability/CondVar.lean index 287665ed244df8..87f0c269da18e4 100644 --- a/Mathlib/Probability/CondVar.lean +++ b/Mathlib/Probability/CondVar.lean @@ -6,6 +6,7 @@ Authors: Yaël Dillies module public import Mathlib.MeasureTheory.Function.ConditionalExpectation.PullOut +public import Mathlib.MeasureTheory.Function.ConditionalExpectation.Real public import Mathlib.MeasureTheory.Integral.Average public import Mathlib.Probability.Moments.Variance @@ -113,8 +114,8 @@ lemma condVar_ae_eq_condExp_sq_sub_sq_condExp (hm : m ≤ m₀) [IsFiniteMeasure have aux₀ : Integrable (X ^ 2) μ := hX.integrable_sq have aux₁ : Integrable (2 * X * μ[X | m]) μ := by rw [mul_assoc] - exact (memLp_one_iff_integrable.1 <| hX.condExp.mul hX).const_mul _ - have aux₂ : Integrable (μ[X | m] ^ 2) μ := hX.condExp.integrable_sq + exact (memLp_one_iff_integrable.1 <| (hX.condExp one_le_two).mul hX).const_mul _ + have aux₂ : Integrable (μ[X | m] ^ 2) μ := (hX.condExp one_le_two).integrable_sq filter_upwards [condExp_add (m := m) (aux₀.sub aux₁) aux₂, condExp_sub (m := m) aux₀ aux₁, condExp_mul_of_stronglyMeasurable_right stronglyMeasurable_condExp aux₁ ((hX.integrable one_le_two).const_mul _), condExp_ofNat (m := m) 2 X] @@ -138,10 +139,10 @@ lemma integral_condVar_add_variance_condExp (hm : m ≤ m₀) [IsProbabilityMeas _ = μ[(μ[X ^ 2 | m] - μ[X | m] ^ 2 : Ω → ℝ)] + (μ[μ[X | m] ^ 2] - μ[μ[X | m]] ^ 2) := by congr 1 · exact integral_congr_ae <| condVar_ae_eq_condExp_sq_sub_sq_condExp hm hX - · exact variance_eq_sub hX.condExp + · exact variance_eq_sub (hX.condExp one_le_two) _ = μ[X ^ 2] - μ[μ[X | m] ^ 2] + (μ[μ[X | m] ^ 2] - μ[X] ^ 2) := by rw [integral_sub' integrable_condExp, integral_condExp hm, integral_condExp hm] - exact hX.condExp.integrable_sq + exact (hX.condExp one_le_two).integrable_sq _ = Var[X; μ] := by rw [variance_eq_sub hX]; ring lemma condVar_bot' [NeZero μ] (X : Ω → ℝ) : diff --git a/Mathlib/Probability/ConditionalProbability.lean b/Mathlib/Probability/ConditionalProbability.lean index ce97e0f5c8dae4..75bba5fe9df894 100644 --- a/Mathlib/Probability/ConditionalProbability.lean +++ b/Mathlib/Probability/ConditionalProbability.lean @@ -6,6 +6,7 @@ Authors: Rishikesh Vaishnav module public import Mathlib.MeasureTheory.Measure.Typeclasses.Probability +public import Mathlib.Tactic.CrossRefAttribute /-! # Conditional Probability @@ -71,6 +72,7 @@ variable (μ) in /-- The conditional probability measure of measure `μ` on set `s` is `μ` restricted to `s` and scaled by the inverse of `μ s` (to make it a probability measure): `(μ s)⁻¹ • μ.restrict s`. -/ +@[wikidata Q327069] def cond (s : Set Ω) : Measure Ω := (μ s)⁻¹ • μ.restrict s diff --git a/Mathlib/Probability/Distributions/Exponential.lean b/Mathlib/Probability/Distributions/Exponential.lean index 5f03844c8318b2..20f6e8d2b2bedc 100644 --- a/Mathlib/Probability/Distributions/Exponential.lean +++ b/Mathlib/Probability/Distributions/Exponential.lean @@ -7,6 +7,7 @@ module public import Mathlib.Probability.CDF public import Mathlib.Probability.Distributions.Gamma +public import Mathlib.Tactic.CrossRefAttribute /-! # Exponential distributions over ℝ @@ -90,6 +91,7 @@ end ExponentialPDF open MeasureTheory /-- Measure defined by the exponential distribution -/ +@[wikidata Q237193] noncomputable def expMeasure (r : ℝ) : Measure ℝ := gammaMeasure 1 r diff --git a/Mathlib/Probability/Distributions/Gaussian/Fernique.lean b/Mathlib/Probability/Distributions/Gaussian/Fernique.lean index f8e1985aea8a16..c911150d3c42e7 100644 --- a/Mathlib/Probability/Distributions/Gaussian/Fernique.lean +++ b/Mathlib/Probability/Distributions/Gaussian/Fernique.lean @@ -220,16 +220,16 @@ lemma eq_dirac_of_variance_eq_zero (h : ∀ L : StrongDual ℝ E, Var[L; μ] = 0 rw [charFunDual_dirac, charFunDual_eq L, h L, integral_complex_ofReal, integral_dual L] simp -/-- If a Gaussian measure is not a Dirac, then it has no atoms. -/ -lemma noAtoms (h : ∀ x, μ ≠ Measure.dirac x) : NoAtoms μ where +/-- If a Gaussian measure is not a Dirac, then it has value zero on singletons. -/ +lemma nullSingletonClass (h : ∀ x, μ ≠ Measure.dirac x) : NullSingletonClass μ where measure_singleton x := by obtain ⟨L, hL⟩ : ∃ L : StrongDual ℝ E, Var[L; μ] ≠ 0 := by contrapose! h exact ⟨_, eq_dirac_of_variance_eq_zero h⟩ have hL_zero : μ.map L {L x} = 0 := by - have : NoAtoms (μ.map L) := by + have : NullSingletonClass (μ.map L) := by rw [map_eq_gaussianReal L] - refine noAtoms_gaussianReal ?_ + refine nullSingletonClass_gaussianReal ?_ simp only [ne_eq, Real.toNNReal_eq_zero, not_le] exact lt_of_le_of_ne (variance_nonneg _ _) hL.symm rw [measure_singleton] @@ -237,6 +237,9 @@ lemma noAtoms (h : ∀ x, μ ≠ Measure.dirac x) : NoAtoms μ where refine measure_mono_null ?_ hL_zero exact fun ⦃a⦄ ↦ congrArg ⇑L +@[deprecated (since := "2026-06-09")] +alias noAtoms := nullSingletonClass + /-- Characteristic function of a centered Gaussian measure. -/ lemma charFunDual_eq_of_integral_eq_zero (hμ : μ[id] = 0) (L : StrongDual ℝ E) : charFunDual μ L = exp (- Var[L; μ] / 2) := by diff --git a/Mathlib/Probability/Distributions/Gaussian/Real.lean b/Mathlib/Probability/Distributions/Gaussian/Real.lean index 9b7b05e1bbfa7b..89ef930b8563ac 100644 --- a/Mathlib/Probability/Distributions/Gaussian/Real.lean +++ b/Mathlib/Probability/Distributions/Gaussian/Real.lean @@ -232,10 +232,14 @@ instance instIsProbabilityMeasureGaussianReal (μ : ℝ) (v : ℝ≥0) : IsProbabilityMeasure (gaussianReal μ v) where measure_univ := by by_cases h : v = 0 <;> simp [gaussianReal_of_var_ne_zero, h] -lemma noAtoms_gaussianReal {μ : ℝ} {v : ℝ≥0} (h : v ≠ 0) : NoAtoms (gaussianReal μ v) := by +lemma nullSingletonClass_gaussianReal {μ : ℝ} {v : ℝ≥0} (h : v ≠ 0) : + NullSingletonClass (gaussianReal μ v) := by rw [gaussianReal_of_var_ne_zero _ h] infer_instance +@[deprecated (since := "2026-06-09")] +alias noAtoms_gaussianReal := nullSingletonClass_gaussianReal + lemma gaussianReal_apply (μ : ℝ) {v : ℝ≥0} (hv : v ≠ 0) (s : Set ℝ) : gaussianReal μ v s = ∫⁻ x in s, gaussianPDF μ v x := by rw [gaussianReal_of_var_ne_zero _ hv, withDensity_apply' _ s] diff --git a/Mathlib/Probability/Distributions/Uniform.lean b/Mathlib/Probability/Distributions/Uniform.lean index ba27e336293991..ccf3e23c43c6fe 100644 --- a/Mathlib/Probability/Distributions/Uniform.lean +++ b/Mathlib/Probability/Distributions/Uniform.lean @@ -11,6 +11,7 @@ public import Mathlib.Probability.ProbabilityMassFunction.Constructions /-! # Uniform distributions and probability mass functions + This file defines two related notions of uniform distributions, which will be unified in the future. ## Uniform distributions diff --git a/Mathlib/Probability/HasCondDistrib.lean b/Mathlib/Probability/HasCondDistrib.lean new file mode 100644 index 00000000000000..68b6d95b1793bb --- /dev/null +++ b/Mathlib/Probability/HasCondDistrib.lean @@ -0,0 +1,124 @@ +/- +Copyright (c) 2026 Rémy Degenne. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Rémy Degenne, Paulo Rauber +-/ + +module + +public import Mathlib.Probability.HasLaw + +import Mathlib.Probability.Kernel.Composition.Lemmas + +/-! +# A predicate for having a specified conditional distribution + +We introduce a predicate `HasCondDistrib Y X κ P` stating that the conditional distribution of `Y` +given `X` under the measure `P` is equal to the kernel `κ`. +The statement uses `HasLaw` to express that the law of the pair `(X, Y)` under `P` is equal to +`(P.map X) ⊗ₘ κ`, the product of the law of `X` under `P` and the kernel `κ`. +The use of `HasLaw` also implies that `Y` and `X` are a.e. measurable. + +## Main definitions + +* `HasCondDistrib Y X κ P` : predicate stating that the conditional distribution of `Y` given `X` + under the measure `P` is equal to the kernel `κ`. + +-/ + +@[expose] public section + +open MeasureTheory + +namespace ProbabilityTheory + +variable {Ω 𝓧 𝓨 𝓩 : Type*} {mΩ : MeasurableSpace Ω} + {m𝓧 : MeasurableSpace 𝓧} {m𝓨 : MeasurableSpace 𝓨} {m𝓩 : MeasurableSpace 𝓩} + {P : Measure Ω} {X : Ω → 𝓧} {Y : Ω → 𝓨} {κ : Kernel 𝓧 𝓨} + +/-- Predicate stating that the conditional distribution of `Y` given `X` under the measure `P` +is equal to the kernel `κ`. -/ +def HasCondDistrib (Y : Ω → 𝓨) (X : Ω → 𝓧) (κ : Kernel 𝓧 𝓨) (P : Measure Ω) : Prop := + HasLaw (fun ω ↦ (X ω, Y ω)) ((P.map X) ⊗ₘ κ) P + +@[fun_prop] +lemma HasCondDistrib.aemeasurable_fst (h : HasCondDistrib Y X κ P) : + AEMeasurable X P := h.aemeasurable.fst + +@[fun_prop] +lemma HasCondDistrib.aemeasurable_snd (h : HasCondDistrib Y X κ P) : + AEMeasurable Y P := h.aemeasurable.snd + +lemma HasLaw.prodMk_of_hasCondDistrib {Q : Measure 𝓧} + (h1 : HasLaw X Q P) (h2 : HasCondDistrib Y X κ P) : + HasLaw (fun ω ↦ (X ω, Y ω)) (Q ⊗ₘ κ) P := by rwa [← h1.map_eq] + +lemma HasCondDistrib.hasLaw_of_const [IsProbabilityMeasure P] {Q : Measure 𝓨} [SFinite Q] + (h : HasCondDistrib Y X (Kernel.const 𝓧 Q) P) : + HasLaw Y Q P where + map_eq := by + have h_snd : (P.map (fun ω ↦ (X ω, Y ω))).snd = Q := by + rw [h.map_eq, Measure.snd_compProd] + simp [Measure.map_apply_of_aemeasurable h.aemeasurable_fst] + rwa [Measure.snd_map_prodMk₀ h.aemeasurable_fst] at h_snd + +variable [SFinite P] [IsSFiniteKernel κ] + +lemma HasCondDistrib.comp_left (h : HasCondDistrib Y X κ P) {f : 𝓨 → 𝓩} (hf : Measurable f) : + HasCondDistrib (f ∘ Y) X (κ.map f) P where + map_eq := calc + P.map (fun ω ↦ (X ω, f (Y ω))) + _ = (P.map (fun ω ↦ (X ω, Y ω))).map (Prod.map id f) := by + rw [AEMeasurable.map_map_of_aemeasurable (by fun_prop) (by fun_prop)] + congr + _ = (P.map X ⊗ₘ κ).map (Prod.map id f) := by rw [h.map_eq] + _ = P.map X ⊗ₘ κ.map f := by rw [Measure.compProd_map hf] + +lemma HasCondDistrib.fst {Y : Ω → 𝓨 × 𝓩} {κ : Kernel 𝓧 (𝓨 × 𝓩)} [IsSFiniteKernel κ] + (h : HasCondDistrib Y X κ P) : + HasCondDistrib (fun ω ↦ (Y ω).1) X κ.fst P := by + rw [Kernel.fst_eq] + exact h.comp_left measurable_fst + +lemma HasCondDistrib.snd {Y : Ω → 𝓨 × 𝓩} {κ : Kernel 𝓧 (𝓨 × 𝓩)} [IsSFiniteKernel κ] + (h : HasCondDistrib Y X κ P) : + HasCondDistrib (fun ω ↦ (Y ω).2) X κ.snd P := by + rw [Kernel.snd_eq] + exact h.comp_left measurable_snd + +lemma HasCondDistrib.comp_right {f : 𝓩 → 𝓧} + {hf : Measurable f} {Z : Ω → 𝓩} (h : HasCondDistrib Y Z (κ.comap f hf) P) : + HasCondDistrib Y (f ∘ Z) κ P where + map_eq := calc + P.map (fun a ↦ ((f ∘ Z) a, Y a)) + _ = (P.map (fun a ↦ (Z a, Y a))).map (Prod.map f id) := by + rw [AEMeasurable.map_map_of_aemeasurable (by fun_prop) (by fun_prop)] + rfl + _ = (P.map Z ⊗ₘ κ.comap f hf).map (Prod.map f id) := by rw [h.map_eq] + _ = (P.map Z).map f ⊗ₘ κ := by + ext s hs + rw [Measure.map_apply (by fun_prop) hs, Measure.compProd_apply (by measurability), + Measure.compProd_apply hs, lintegral_map (Kernel.measurable_kernel_prodMk_left hs) hf] + rfl + _ = P.map (f ∘ Z) ⊗ₘ κ := by + rw [AEMeasurable.map_map_of_aemeasurable hf.aemeasurable (by fun_prop)] + +lemma HasCondDistrib.measurableEquiv_comp_right (h : HasCondDistrib Y X κ P) (f : 𝓧 ≃ᵐ 𝓩) : + HasCondDistrib Y (f ∘ X) (κ.comap f.symm f.symm.measurable) P := by + apply HasCondDistrib.comp_right (hf := f.measurable) + simpa [← Kernel.comap_comp_right] + +lemma HasCondDistrib.of_compProd {Z : Ω → 𝓩} {η : Kernel (𝓧 × 𝓨) 𝓩} [IsMarkovKernel η] + (h : HasCondDistrib (fun a ↦ (Y a, Z a)) X (κ ⊗ₖ η) P) : + HasCondDistrib Z (fun a ↦ (X a, Y a)) η P := by + have hZ : AEMeasurable Z P := h.aemeasurable_snd.snd + have hY : AEMeasurable Y P := h.aemeasurable_snd.fst + refine ⟨by fun_prop, ?_⟩ + calc P.map (fun a ↦ ((X a, Y a), Z a)) + _ = (P.map X ⊗ₘ (κ ⊗ₖ η)).map MeasurableEquiv.prodAssoc.symm := by + rw [← h.map_eq, AEMeasurable.map_map_of_aemeasurable (by fun_prop) (by fun_prop)] + rfl + _ = P.map X ⊗ₘ κ ⊗ₘ η := Measure.compProd_assoc + _ = P.map (fun a ↦ (X a, Y a)) ⊗ₘ η := by simp [h.fst.map_eq] + +end ProbabilityTheory diff --git a/Mathlib/Probability/Independence/Basic.lean b/Mathlib/Probability/Independence/Basic.lean index 680f9930fa7ed5..41ce7f824cf9d6 100644 --- a/Mathlib/Probability/Independence/Basic.lean +++ b/Mathlib/Probability/Independence/Basic.lean @@ -1036,7 +1036,7 @@ theorem iIndepSet.iIndepFun_indicator [Zero β] [One β] {m : MeasurableSpace β Kernel.iIndepSet.iIndepFun_indicator hs lemma Indep.indicator_indepFun {m : MeasurableSpace Ω} {M 𝓧 : Type*} - [Zero M] [MeasurableSpace M] (c : M) [NeZero c] {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω} + [Zero M] [MeasurableSpace M] (c : M) {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω} {X : Ω → 𝓧} (hA : MeasurableSet[m] A) (h : Indep m (m𝓧.comap X) μ) : (A.indicator (fun _ ↦ c)) ⟂ᵢ[μ] X := Kernel.Indep.indicator_const_indepFun c hA h diff --git a/Mathlib/Probability/Independence/Conditional.lean b/Mathlib/Probability/Independence/Conditional.lean index 5200e504eb8cb3..6d834c8d87db06 100644 --- a/Mathlib/Probability/Independence/Conditional.lean +++ b/Mathlib/Probability/Independence/Conditional.lean @@ -230,7 +230,7 @@ lemma condIndepSets_iff (s1 s2 : Set (Set Ω)) (hs1 : ∀ s ∈ s1, MeasurableSe filter_upwards [hs1_eq s hs, hs2_eq t ht, hs12_eq s hs t ht, h'] with ω hs_eq ht_eq hst_eq h' rw [← hst_eq, Pi.mul_apply, ← hs_eq, ← ht_eq, h', ENNReal.toReal_mul] · refine ((stronglyMeasurable_condExpKernel ((hs1 s hs).inter (hs2 t ht))).ae_eq_trim_iff hm' - ((measurable_condExpKernel (hs1 s hs)).mul + ((measurable_condExpKernel (hs1 s hs)).fun_mul (measurable_condExpKernel (hs2 t ht))).stronglyMeasurable).mpr ?_ filter_upwards [hs1_eq s hs, hs2_eq t ht, hs12_eq s hs t ht, h] with ω hs_eq ht_eq hst_eq h have h_ne_top : condExpKernel μ m' ω (s ∩ t) ≠ ∞ := measure_ne_top (condExpKernel μ m' ω) _ diff --git a/Mathlib/Probability/Independence/Integration.lean b/Mathlib/Probability/Independence/Integration.lean index b3e84ccd379466..36bfa475e2706e 100644 --- a/Mathlib/Probability/Independence/Integration.lean +++ b/Mathlib/Probability/Independence/Integration.lean @@ -95,8 +95,8 @@ theorem lintegral_mul_eq_lintegral_mul_lintegral_of_independent_measurableSpace · intro f' g _ h_measMg_f' _ h_ind_f' h_ind_g' have h_measM_f' : Measurable f' := h_measMg_f'.mono hMg le_rfl simp_rw [Pi.add_apply, left_distrib] - rw [lintegral_add_left h_measM_f', lintegral_add_left (h_measM_f.mul h_measM_f'), left_distrib, - h_ind_f', h_ind_g'] + rw [lintegral_add_left h_measM_f', + lintegral_add_left (h_measM_f.fun_mul h_measM_f'), left_distrib, h_ind_f', h_ind_g'] · intro f' h_meas_f' h_mono_f' h_ind_f' have h_measM_f' : ∀ n, Measurable (f' n) := fun n => (h_meas_f' n).mono hMg le_rfl simp_rw [mul_iSup] @@ -312,11 +312,11 @@ theorem IndepFun.integral_bilin_comp_comp' /-- If `X` and `Y` are independent and integrable random variables and `B` is a continuous bilinear map, then `∫ ω, B (X ω) (Y ω) ∂μ = B μ[X] μ[Y].` -/ theorem IndepFun.integral_bilin - [NormedAddCommGroup E] [NormedSpace ℝ E] [NormedSpace 𝕜 E] [CompleteSpace E] + [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E] [MeasurableSpace E] [BorelSpace E] - [NormedAddCommGroup F] [NormedSpace ℝ F] [NormedSpace 𝕜 F] [CompleteSpace F] + [NormedAddCommGroup F] [NormedSpace ℝ F] [CompleteSpace F] [MeasurableSpace F] [BorelSpace F] - [NormedAddCommGroup G] [NormedSpace ℝ G] [NormedSpace 𝕜 G] [CompleteSpace G] + [NormedAddCommGroup G] [NormedSpace ℝ G] [CompleteSpace G] {X : Ω → E} {Y : Ω → F} (hXY : X ⟂ᵢ[μ] Y) (hX : Integrable X μ) (hY : Integrable Y μ) (B : E →L[ℝ] F →L[ℝ] G) : ∫ ω, B (X ω) (Y ω) ∂μ = B μ[X] μ[Y] := @@ -333,11 +333,11 @@ The assumption on `B` allows to drop the integrability condition in `IndepFun.integral_bilin'`, which is useful for the versions where `B` is the scalar multiplication or the multiplication. -/ theorem IndepFun.integral_bilin' - [NormedAddCommGroup E] [NormedSpace ℝ E] [NormedSpace 𝕜 E] [CompleteSpace E] + [NormedAddCommGroup E] [NormedSpace ℝ E] [CompleteSpace E] [MeasurableSpace E] [BorelSpace E] - [NormedAddCommGroup F] [NormedSpace ℝ F] [NormedSpace 𝕜 F] [CompleteSpace F] + [NormedAddCommGroup F] [NormedSpace ℝ F] [CompleteSpace F] [MeasurableSpace F] [BorelSpace F] - [NormedAddCommGroup G] [NormedSpace ℝ G] [NormedSpace 𝕜 G] [CompleteSpace G] + [NormedAddCommGroup G] [NormedSpace ℝ G] [CompleteSpace G] {X : Ω → E} {Y : Ω → F} (hXY : X ⟂ᵢ[μ] Y) (hX : AEStronglyMeasurable X μ) (hY : AEStronglyMeasurable Y μ) (B : E →L[ℝ] F →L[ℝ] G) (c : ℝ≥0) (hc : c ≠ 0) (hB : ∀ x y, c * ‖x‖ * ‖y‖ ≤ ‖B x y‖) : @@ -405,7 +405,7 @@ lemma IndepFun.integral_smul_eq_smul_integral (hX : AEStronglyMeasurable X μ) (hY : AEStronglyMeasurable Y μ) : μ[X • Y] = μ[X] • μ[Y] := by by_cases hE : CompleteSpace E - · exact hXY.integral_bilin' (𝕜 := 𝕜) hX hY (.lsmul ℝ 𝕜) 1 (by simp) (by simp [norm_smul]) + · exact hXY.integral_bilin' hX hY (.lsmul ℝ 𝕜) 1 (by simp) (by simp [norm_smul]) · simp [integral, hE] lemma IndepFun.integral_mul_eq_mul_integral diff --git a/Mathlib/Probability/Independence/Kernel/IndepFun.lean b/Mathlib/Probability/Independence/Kernel/IndepFun.lean index bd59b14882e064..56935576fa75f9 100644 --- a/Mathlib/Probability/Independence/Kernel/IndepFun.lean +++ b/Mathlib/Probability/Independence/Kernel/IndepFun.lean @@ -695,7 +695,7 @@ theorem iIndepSet.iIndepFun_indicator [Zero β] [One β] {m : MeasurableSpace β · exact @MeasurableSet.empty _ (generateFrom {s i}) lemma Indep.indicator_const_indepFun {m : MeasurableSpace Ω} {M 𝓧 : Type*} - [Zero M] [MeasurableSpace M] (c : M) [NeZero c] {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω} + [Zero M] [MeasurableSpace M] (c : M) {m𝓧 : MeasurableSpace 𝓧} {A : Set Ω} {X : Ω → 𝓧} (hA : MeasurableSet[m] A) (h : Indep m (m𝓧.comap X) κ μ) : IndepFun (A.indicator (fun _ ↦ c)) X κ μ := indep_of_indep_of_le_left h (measurable_const.indicator hA).comap_le diff --git a/Mathlib/Probability/Kernel/Proper.lean b/Mathlib/Probability/Kernel/Proper.lean index dfea5904dda58c..61beb895b36e20 100644 --- a/Mathlib/Probability/Kernel/Proper.lean +++ b/Mathlib/Probability/Kernel/Proper.lean @@ -135,7 +135,7 @@ lemma IsProper.lintegral_mul (hπ : IsProper π) (h𝓑𝓧 : 𝓑 ≤ 𝓧) (hf · exact (hg₂_meas.mono h𝓑𝓧 le_rfl).mul hf · rintro g' hg'_meas hg'_mono hg' simp_rw [ENNReal.iSup_mul] - rw [lintegral_iSup (fun n ↦ ((hg'_meas _).mono h𝓑𝓧 le_rfl).mul hf) + rw [lintegral_iSup (fun n ↦ ((hg'_meas _).mono h𝓑𝓧 le_rfl).fun_mul hf) (hg'_mono.mul_const zero_le)] simp_rw [hg'] diff --git a/Mathlib/Probability/Martingale/BorelCantelli.lean b/Mathlib/Probability/Martingale/BorelCantelli.lean index ef5a464411616d..ae70ff31aa470b 100644 --- a/Mathlib/Probability/Martingale/BorelCantelli.lean +++ b/Mathlib/Probability/Martingale/BorelCantelli.lean @@ -283,10 +283,11 @@ theorem tendsto_sum_indicator_atTop_iff [IsFiniteMeasure μ] (hint : ∀ n, Integrable (f n) μ) (hbdd : ∀ᵐ ω ∂μ, ∀ n, |f (n + 1) ω - f n ω| ≤ R) : ∀ᵐ ω ∂μ, Tendsto (fun n => f n ω) atTop atTop ↔ Tendsto (fun n => predictablePart f ℱ μ n ω) atTop atTop := by - have h₁ := (martingale_martingalePart hf hint).ae_not_tendsto_atTop_atTop - (martingalePart_bdd_difference ℱ hbdd) - have h₂ := (martingale_martingalePart hf hint).ae_not_tendsto_atTop_atBot - (martingalePart_bdd_difference ℱ hbdd) + simp only [← Real.norm_eq_abs] at hbdd + have h₀ := martingalePart_bdd_difference ℱ hbdd + simp only [Real.norm_eq_abs, ← NNReal.coe_ofNat, ← NNReal.coe_mul 2 R] at h₀ + have h₁ := (martingale_martingalePart hf hint).ae_not_tendsto_atTop_atTop h₀ + have h₂ := (martingale_martingalePart hf hint).ae_not_tendsto_atTop_atBot h₀ have h₃ : ∀ᵐ ω ∂μ, ∀ n, 0 ≤ (μ[f (n + 1) - f n | ℱ n]) ω := by refine ae_all_iff.2 fun n => condExp_nonneg ?_ filter_upwards [ae_all_iff.1 hfmono n] with ω hω using sub_nonneg.2 hω diff --git a/Mathlib/Probability/Martingale/Centering.lean b/Mathlib/Probability/Martingale/Centering.lean index 4fdfd483e7f7a0..09243a048a6c1f 100644 --- a/Mathlib/Probability/Martingale/Centering.lean +++ b/Mathlib/Probability/Martingale/Centering.lean @@ -99,8 +99,8 @@ lemma Submartingale.predictablePart_nonneg filter_upwards [hf.monotone_predictablePart] with ω hω n simpa [predictablePart_zero] using hω (Nat.zero_le n) -lemma IsStronglyPredictable.predictablePart_eq [SecondCountableTopology E] [MeasurableSpace E] - [BorelSpace E] [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f) +lemma IsStronglyPredictable.predictablePart_eq + [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f) (hfint : ∀ n, Integrable (f n) μ) (n : ℕ) : predictablePart f ℱ μ n =ᵐ[μ] f n - f 0 := by simp only [predictablePart, ← Finset.sum_range_sub] @@ -148,8 +148,7 @@ lemma Martingale.martingalePart_eq [CompleteSpace E] (hf : Martingale f ℱ μ) filter_upwards [hf.predictablePart_eq_zero n] with ω hω simp [martingalePart, hω] -lemma IsPredictable.martingalePart_eq [SecondCountableTopology E] [MeasurableSpace E] - [BorelSpace E] [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f) +lemma IsPredictable.martingalePart_eq [SigmaFiniteFiltration μ ℱ] (hf : IsStronglyPredictable ℱ f) (hfint : ∀ n, Integrable (f n) μ) (n : ℕ) : martingalePart f ℱ μ n =ᵐ[μ] f 0 := by filter_upwards [hf.predictablePart_eq (μ := μ) hfint n] with ω hω @@ -245,22 +244,18 @@ theorem predictablePart_add_ae_eq [CompleteSpace E] [SigmaFiniteFiltration μ section Difference -theorem predictablePart_bdd_difference {R : ℝ≥0} {f : ℕ → Ω → ℝ} (ℱ : Filtration ℕ m0) - (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) : - ∀ᵐ ω ∂μ, ∀ i, |predictablePart f ℱ μ (i + 1) ω - predictablePart f ℱ μ i ω| ≤ R := by +theorem predictablePart_bdd_difference [CompleteSpace E] {R : ℝ} {f : ℕ → Ω → E} + (ℱ : Filtration ℕ m0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, ‖f (i + 1) ω - f i ω‖ ≤ R) : + ∀ᵐ ω ∂μ, ∀ i, ‖predictablePart f ℱ μ (i + 1) ω - predictablePart f ℱ μ i ω‖ ≤ R := by simp_rw [predictablePart, Finset.sum_apply, Finset.sum_range_succ_sub_sum] - exact ae_all_iff.2 fun i => ae_bdd_condExp_of_ae_bdd <| ae_all_iff.1 hbdd i + exact ae_all_iff.2 fun i => ae_bdd_norm_condExp_of_ae_bdd_norm <| ae_all_iff.1 hbdd i -theorem martingalePart_bdd_difference {R : ℝ≥0} {f : ℕ → Ω → ℝ} (ℱ : Filtration ℕ m0) - (hbdd : ∀ᵐ ω ∂μ, ∀ i, |f (i + 1) ω - f i ω| ≤ R) : - ∀ᵐ ω ∂μ, ∀ i, |martingalePart f ℱ μ (i + 1) ω - martingalePart f ℱ μ i ω| ≤ ↑(2 * R) := by +theorem martingalePart_bdd_difference [CompleteSpace E] {R : ℝ} {f : ℕ → Ω → E} + (ℱ : Filtration ℕ m0) (hbdd : ∀ᵐ ω ∂μ, ∀ i, ‖f (i + 1) ω - f i ω‖ ≤ R) : + ∀ᵐ ω ∂μ, ∀ i, ‖martingalePart f ℱ μ (i + 1) ω - martingalePart f ℱ μ i ω‖ ≤ 2 * R := by filter_upwards [hbdd, predictablePart_bdd_difference ℱ hbdd] with ω hω₁ hω₂ i - simp only [two_mul, martingalePart, Pi.sub_apply] - have : |f (i + 1) ω - predictablePart f ℱ μ (i + 1) ω - (f i ω - predictablePart f ℱ μ i ω)| = - |f (i + 1) ω - f i ω - (predictablePart f ℱ μ (i + 1) ω - predictablePart f ℱ μ i ω)| := by - ring_nf -- `ring` suggests `ring_nf` despite proving the goal - rw [this] - exact (abs_sub _ _).trans (add_le_add (hω₁ i) (hω₂ i)) + simpa [two_mul, martingalePart, sub_sub_sub_comm] using + (norm_sub_le _ _).trans (add_le_add (hω₁ i) (hω₂ i)) end Difference diff --git a/Mathlib/Probability/Martingale/Convergence.lean b/Mathlib/Probability/Martingale/Convergence.lean index 580e50c3b1d632..f304cfd1f359f9 100644 --- a/Mathlib/Probability/Martingale/Convergence.lean +++ b/Mathlib/Probability/Martingale/Convergence.lean @@ -335,7 +335,7 @@ theorem Martingale.eq_condExp_of_tendsto_eLpNorm {μ : Measure Ω} (hf : Marting have ht : Tendsto (fun m => eLpNorm (μ[f m - g | ℱ n]) 1 μ) atTop (𝓝 0) := haveI hint : ∀ m, Integrable (f m - g) μ := fun m => (hf.integrable m).sub hg tendsto_of_tendsto_of_tendsto_of_le_of_le tendsto_const_nhds hgtends (fun m => zero_le) - fun m => eLpNorm_one_condExp_le_eLpNorm _ + fun m => eLpNorm_condExp_le_eLpNorm _ le_rfl have hev : ∀ m ≥ n, eLpNorm (μ[f m - g | ℱ n]) 1 μ = eLpNorm (f n - μ[g | ℱ n]) 1 μ := by refine fun m hm => eLpNorm_congr_ae ((condExp_sub (hf.integrable m) hg _).trans ?_) filter_upwards [hf.2 n m hm] with x hx diff --git a/Mathlib/Probability/Moments/CovarianceBilinDual.lean b/Mathlib/Probability/Moments/CovarianceBilinDual.lean index 4e017ec7919830..b6270b417c8f6b 100644 --- a/Mathlib/Probability/Moments/CovarianceBilinDual.lean +++ b/Mathlib/Probability/Moments/CovarianceBilinDual.lean @@ -91,7 +91,7 @@ lemma norm_toLpₗ_le [OpensMeasurableSpace E] (L : StrongDual 𝕜 E) : gcongr · rw [ENNReal.essSup_const_mul] exact ENNReal.mul_ne_top (by simp) h_Lp.eLpNorm_ne_top - · exact essSup_mono_ae <| ae_of_all _ L.le_opNorm_enorm + · exact essSup_mono_ae <| ae_of_all _ L.le_opENorm have h0 : 0 < p.toReal := by simp [ENNReal.toReal_pos_iff, pos_iff_ne_zero, hp, Ne.lt_top hp_top] suffices ‖L.toLpₗ μ p‖ ≤ (‖L‖ₑ ^ p.toReal * ∫⁻ x, ‖x‖ₑ ^ p.toReal ∂μ).toReal ^ p.toReal⁻¹ by @@ -119,7 +119,7 @@ lemma norm_toLpₗ_le [OpensMeasurableSpace E] (L : StrongDual 𝕜 E) : rw [← ENNReal.mul_rpow_of_nonneg] swap; · positivity gcongr - exact L.le_opNorm_enorm x + exact L.le_opENorm x _ = ‖L‖ₑ ^ p.toReal * ∫⁻ x, ‖x‖ₑ ^ p.toReal ∂μ := by rw [lintegral_const_mul]; fun_prop end LinearMap diff --git a/Mathlib/Probability/Moments/SubGaussian.lean b/Mathlib/Probability/Moments/SubGaussian.lean index 7751694c849b57..0de6c2ca8428f9 100644 --- a/Mathlib/Probability/Moments/SubGaussian.lean +++ b/Mathlib/Probability/Moments/SubGaussian.lean @@ -852,7 +852,7 @@ lemma hasSubgaussianMGF_of_mem_Icc_of_integral_eq_zero [IsProbabilityMeasure μ] _ ≤ exp ((‖-a - -b‖₊ / 2) ^ 2 * (-t) ^ 2 / 2) := by apply ProbabilityTheory.mgf_le_of_mem_Icc_of_integral_eq_zero (hm.neg) · filter_upwards [hb] with ω ⟨hl, hr⟩ using ⟨neg_le_neg_iff.2 hr, neg_le_neg_iff.2 hl⟩ - · rw [integral_neg, hc, neg_zero] + · simp only [Pi.neg_apply]; rw [integral_neg, hc, neg_zero] · rwa [Left.neg_pos_iff] _ = exp (((‖b - a‖₊ / 2) ^ 2) * t ^ 2 / 2) := by ring_nf diff --git a/Mathlib/RepresentationTheory/Basic.lean b/Mathlib/RepresentationTheory/Basic.lean index b7ba95cae0da1a..ffa02a90949dc8 100644 --- a/Mathlib/RepresentationTheory/Basic.lean +++ b/Mathlib/RepresentationTheory/Basic.lean @@ -101,6 +101,10 @@ theorem self_inv_apply (g : G) (x : V) : ρ g (ρ g⁻¹ x) = x := by simp [← Module.End.mul_apply, ← map_mul] +lemma inv_apply_eq_iff {g : G} {x y : V} : + ρ g⁻¹ x = y ↔ x = ρ g y := by + constructor <;> rintro rfl <;> simp + lemma apply_bijective (g : G) : Function.Bijective (ρ g) := Equiv.bijective ⟨ρ g, ρ g⁻¹, inv_self_apply ρ g, self_inv_apply ρ g⟩ diff --git a/Mathlib/RepresentationTheory/Character.lean b/Mathlib/RepresentationTheory/Character.lean index 5b1acb384378ab..768f3d7f3dd200 100644 --- a/Mathlib/RepresentationTheory/Character.lean +++ b/Mathlib/RepresentationTheory/Character.lean @@ -27,7 +27,7 @@ Irreducible representations are implemented categorically, using the `CategoryTh defined in `Mathlib/CategoryTheory/Simple.lean` ## TODO -* Once we have the monoidal closed structure on `FdRep k G` and a better API for the rigid +* Once we have the monoidal closed structure on `FDRep k G` and a better API for the rigid structure, `char_dual` and `char_linHom` should probably be stated in terms of `Vᘁ` and `ihom V W`. -/ @@ -43,102 +43,6 @@ open CategoryTheory LinearMap CategoryTheory.MonoidalCategory Representation Mod variable {k : Type u} [Field k] -namespace FDRep - -section Monoid - -variable {G : Type v} [Monoid G] - -/-- The character of a representation `V : FDRep k G` is the function associating to `g : G` the -trace of the linear map `V.ρ g`. -/ -def character (V : FDRep k G) (g : G) := - LinearMap.trace k V (V.ρ g) - -theorem char_mul_comm (V : FDRep k G) (g : G) (h : G) : - V.character (h * g) = V.character (g * h) := by simp only [trace_mul_comm, character, map_mul] - -@[simp] -theorem char_one (V : FDRep k G) : V.character 1 = Module.finrank k V := by - simp only [character, map_one, trace_one] - -/-- The character is multiplicative under the tensor product. -/ -@[simp] -theorem char_tensor (V W : FDRep k G) : (V ⊗ W).character = V.character * W.character := by - ext g; convert! trace_tensorProduct' (V.ρ g) (W.ρ g) - -/-- The character of isomorphic representations is the same. -/ -theorem char_iso {V W : FDRep k G} (i : V ≅ W) : V.character = W.character := by - ext g - simp only [character, FDRep.Iso.conj_ρ i] - exact (trace_conj' (V.ρ g) _).symm - -end Monoid - -section Group - -variable {G : Type v} [Group G] - -/-- The character of a representation is constant on conjugacy classes. -/ -@[simp] -theorem char_conj (V : FDRep k G) (g : G) (h : G) : V.character (h * g * h⁻¹) = V.character g := by - rw [char_mul_comm, inv_mul_cancel_left] - -@[simp] -theorem char_dual (V : FDRep k G) (g : G) : (of (dual V.ρ)).character g = V.character g⁻¹ := - trace_transpose' (V.ρ g⁻¹) - -@[simp] -theorem char_linHom (V W : FDRep k G) (g : G) : - (of (linHom V.ρ W.ρ)).character g = V.character g⁻¹ * W.character g := by - rw [← char_iso (dualTensorIsoLinHom _ _), char_tensor, Pi.mul_apply, char_dual] - -variable [Fintype G] [Invertible (Fintype.card G : k)] - -theorem average_char_eq_finrank_invariants (V : FDRep k G) : - ⅟(Fintype.card G : k) • ∑ g : G, V.character g = finrank k (invariants V.ρ) := by - rw [← (isProj_averageMap V.ρ).trace] - simp [character, GroupAlgebra.average, _root_.map_sum] - -/-- -If `V` are `W` are finite-dimensional representations of a finite group, then the -scalar product of their characters is equal to the dimension of the space of -equivariant maps from `V` to `W`. --/ -theorem scalar_product_char_eq_finrank_equivariant (V W : FDRep k G) : - ⅟(Fintype.card G : k) • ∑ g : G, W.character g * V.character g⁻¹ = - Module.finrank k (V ⟶ W) := by - conv_lhs => congr; rfl; congr; rfl; intro _; rw [mul_comm, ← FDRep.char_linHom] - -- The scalar product is the character of `Hom(V, W).` - rw [FDRep.average_char_eq_finrank_invariants, ← LinearEquiv.finrank_eq - (Representation.linHom.invariantsEquivFDRepHom V W), of_ρ'] - -- The average over the group of the character of a representation equals the dimension of the - -- space of invariants, and the space of invariants of `Hom(W, V)` is the subspace of - --`G`-equivariant linear maps, `Hom_G(W, V)`. - -end Group - -section Orthogonality - -variable {G : Type v} [Group G] [IsAlgClosed k] - -variable [Fintype G] [Invertible (Fintype.card G : k)] - -open scoped Classical in -/-- Orthogonality of characters for irreducible representations of finite group over an -algebraically closed field whose characteristic doesn't divide the order of the group. -/ -theorem char_orthonormal (V W : FDRep k G) [Simple V] [Simple W] : - ⅟(Fintype.card G : k) • ∑ g : G, V.character g * W.character g⁻¹ = - if Nonempty (V ≅ W) then ↑1 else ↑0 := by - rw [scalar_product_char_eq_finrank_equivariant] - -- The scalar products of the characters is equal to the dimension of the space of - -- equivariant maps `W ⟶ V`. - rw_mod_cast [finrank_hom_simple_simple W V, Iso.nonempty_iso_symm] - -- By Schur's Lemma, the dimension of `Hom_G(W, V)` is `1` is `V ≅ W` and `0` otherwise. - -end Orthogonality - -end FDRep - namespace Representation section Monoid @@ -204,7 +108,7 @@ theorem card_inv_mul_sum_char_eq_finrank : simp [character, GroupAlgebra.average, _root_.map_sum] /-- -If `V` are `W` are finite-dimensional representations of a finite group, then the +If `V` and `W` are finite-dimensional representations of a finite group, then the scalar product of their characters is equal to the dimension of the space of equivariant maps from `V` to `W`. -/ @@ -240,3 +144,101 @@ theorem char_orthonormal [IsIrreducible ρ] [IsIrreducible σ] : end Orthogonality end Representation + +namespace FDRep + +section Monoid + +variable {G : Type v} [Monoid G] + +/-- The character of a representation `V : FDRep k G` is the function associating to `g : G` the +trace of the linear map `V.ρ g`. -/ +def character (V : FDRep k G) (g : G) := + LinearMap.trace k V (V.ρ g) + +theorem char_mul_comm (V : FDRep k G) (g : G) (h : G) : + V.character (h * g) = V.character (g * h) := by simp only [trace_mul_comm, character, map_mul] + +@[simp] +theorem char_one (V : FDRep k G) : V.character 1 = Module.finrank k V := by + simp only [character, map_one, trace_one] + +/-- The character is multiplicative under the tensor product. -/ +@[simp] +theorem char_tensor (V W : FDRep k G) : (V ⊗ W).character = V.character * W.character := by + ext g; convert! trace_tensorProduct' (V.ρ g) (W.ρ g) + +/-- The character of isomorphic representations is the same. -/ +theorem char_iso {V W : FDRep k G} (i : V ≅ W) : V.character = W.character := by + ext g + simp only [character, FDRep.Iso.conj_ρ i] + exact (trace_conj' (V.ρ g) _).symm + +end Monoid + +section Group + +variable {G : Type v} [Group G] + +/-- The character of a representation is constant on conjugacy classes. -/ +@[simp] +theorem char_conj (V : FDRep k G) (g : G) (h : G) : V.character (h * g * h⁻¹) = V.character g := by + rw [char_mul_comm, inv_mul_cancel_left] + +@[simp] +theorem char_dual (V : FDRep k G) (g : G) : (of (dual V.ρ)).character g = V.character g⁻¹ := + trace_transpose' (V.ρ g⁻¹) + +@[simp] +theorem char_linHom (V W : FDRep k G) (g : G) : + (of (linHom V.ρ W.ρ)).character g = V.character g⁻¹ * W.character g := by + rw [← char_iso (dualTensorIsoLinHom _ _), char_tensor, Pi.mul_apply, char_dual] + +variable [Fintype G] [Invertible (Nat.card G : k)] + +theorem average_char_eq_finrank_invariants (V : FDRep k G) : + (Nat.card G : k)⁻¹ * ∑ g : G, V.character g = finrank k (invariants V.ρ) := by + have : Invertible (Fintype.card G : k) := by + rwa [Fintype.card_eq_nat_card] + rw [← (isProj_averageMap V.ρ).trace] + simp [character, GroupAlgebra.average, _root_.map_sum] + +/-- +If `V` and `W` are finite-dimensional representations of a finite group, then the +scalar product of their characters is equal to the dimension of the space of +equivariant maps from `V` to `W`. +-/ +theorem scalar_product_char_eq_finrank_equivariant (V W : FDRep k G) : + (Nat.card G : k)⁻¹ * ∑ g : G, W.character g * V.character g⁻¹ = + Module.finrank k (V ⟶ W) := by + conv_lhs => congr; rfl; congr; rfl; intro _; rw [mul_comm, ← FDRep.char_linHom] + -- The scalar product is the character of `Hom(V, W).` + rw [FDRep.average_char_eq_finrank_invariants, ← LinearEquiv.finrank_eq + (Representation.linHom.invariantsEquivFDRepHom V W), of_ρ'] + -- The average over the group of the character of a representation equals the dimension of the + -- space of invariants, and the space of invariants of `Hom(V, W)` is the subspace of + -- `G`-equivariant linear maps, `Hom_G(V, W)`. + +end Group + +section Orthogonality + +variable {G : Type v} [Group G] [IsAlgClosed k] + +variable [Fintype G] [Invertible (Nat.card G : k)] + +open scoped Classical in +/-- Orthogonality of characters for irreducible representations of finite group over an +algebraically closed field whose characteristic doesn't divide the order of the group. -/ +theorem char_orthonormal (V W : FDRep k G) [Simple V] [Simple W] : + (Nat.card G : k)⁻¹ * ∑ g : G, V.character g * W.character g⁻¹ = + if Nonempty (V ≅ W) then ↑1 else ↑0 := by + rw [scalar_product_char_eq_finrank_equivariant] + -- The scalar product of the characters is equal to the dimension of the space of + -- equivariant maps `W ⟶ V`. + rw_mod_cast [finrank_hom_simple_simple W V, Iso.nonempty_iso_symm] + -- By Schur's Lemma, the dimension of `Hom_G(W, V)` is `1` if `V ≅ W` and `0` otherwise. + +end Orthogonality + +end FDRep diff --git a/Mathlib/RepresentationTheory/Coinduced.lean b/Mathlib/RepresentationTheory/Coinduced.lean index e23be2bcc66fa5..51fb493a80f113 100644 --- a/Mathlib/RepresentationTheory/Coinduced.lean +++ b/Mathlib/RepresentationTheory/Coinduced.lean @@ -128,13 +128,11 @@ noncomputable abbrev coindMap {A B : Rep k G} (f : A ⟶ B) : coind φ A ⟶ coi variable (k) in /-- Given a monoid homomorphism `φ : G →* H`, this is the functor sending a `G`-representation `A` to the coinduced `H`-representation `coind φ A`, with action on maps given by postcomposition. -/ -@[simps obj map] +@[implicit_reducible, simps obj map] noncomputable def coindFunctor : Rep.{t} k G ⥤ Rep k H where obj A := coind φ A map f := coindMap φ f -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in instance {G : Type v'} [Group G] (S : Subgroup G) : (coindFunctor k S.subtype).PreservesEpimorphisms where preserves {X Y} f := (epi_iff_surjective _).2 fun y => by @@ -199,7 +197,7 @@ noncomputable def coindMap' {A B : Rep k G} (f : A ⟶ B) : coind' φ A ⟶ coin variable (k) in /-- Given a monoid homomorphism `φ : G →* H`, this is the functor sending a `G`-representation `A` to the coinduced `H`-representation `coind' φ A`, with action on maps given by postcomposition. -/ -@[simps obj map] +@[implicit_reducible, simps obj map] noncomputable def coindFunctor' : Rep k G ⥤ Rep k H where obj A := coind' φ A map f := coindMap' φ f @@ -228,8 +226,6 @@ noncomputable def coindVEquiv : noncomputable def coindIso : coind φ A ≅ coind' φ A := Rep.mkIso <| .mk (coindVEquiv φ A) fun h => by ext; simp [homEquiv] -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in /-- Given a monoid homomorphism `φ : G →* H`, the coinduction functors `Rep k G ⥤ Rep k H` given by `coindFunctor k φ` and `coindFunctor' k φ` are naturally isomorphic, with isomorphism on objects given by `coindIso φ`. -/ diff --git a/Mathlib/RepresentationTheory/Coinvariants.lean b/Mathlib/RepresentationTheory/Coinvariants.lean index 93c225bec61f57..7182029e0d4dc9 100644 --- a/Mathlib/RepresentationTheory/Coinvariants.lean +++ b/Mathlib/RepresentationTheory/Coinvariants.lean @@ -343,7 +343,7 @@ end variable (k G) [Monoid G] (A B : Rep.{w} k G) /-- The functor sending a representation to its coinvariants. -/ -@[simps! obj_carrier map_hom] +@[implicit_reducible, simps! obj_carrier map_hom] noncomputable def coinvariantsFunctor : Rep.{w} k G ⥤ ModuleCat k where obj A := ModuleCat.of k A.ρ.Coinvariants map f := ModuleCat.ofHom (Representation.Coinvariants.map _ _ f.hom) @@ -378,7 +378,6 @@ instance : (coinvariantsFunctor k G).Additive where instance : (coinvariantsFunctor k G).Linear k where set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in /-- The adjunction between the functor sending a representation to its coinvariants and the functor equipping a module with the trivial representation. -/ @[simps] @@ -394,7 +393,6 @@ theorem coinvariantsAdjunction_homEquiv_apply_hom {X : Rep.{w} k G} {Y : ModuleC rfl set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in @[simp] theorem coinvariantsAdjunction_homEquiv_symm_apply_hom {X : Rep.{w} k G} {Y : ModuleCat k} (f : X ⟶ (trivialFunctor k G).obj Y) : @@ -437,7 +435,6 @@ section variable (k : Type u) {G : Type v} [CommRing k] [Group G] -set_option backward.isDefEq.respectTransparency false in /-- Given a normal subgroup `S ≤ G`, this is the functor sending a `G`-representation `A` to the `G ⧸ S`-representation it induces on `A_S`. -/ @[simps! obj_V map_hom_toLinearMap] @@ -466,7 +463,6 @@ noncomputable def coinvariantsTensorFreeToFinsupp : variable {α} -set_option backward.isDefEq.respectTransparency false in @[simp] lemma coinvariantsTensorFreeToFinsupp_mk_tmul_single (x : A) (i : α) (g : G) (r : k) : DFunLike.coe (F := (A.ρ.tprod (Representation.free k G α)).Coinvariants →ₗ[k] α →₀ A.V) @@ -487,7 +483,6 @@ noncomputable def finsuppToCoinvariantsTensorFree : variable {A α} set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in @[simp] lemma finsuppToCoinvariantsTensorFree_single (i : α) (x : A) : DFunLike.coe (F := (α →₀ A.V) →ₗ[k] (A.ρ.tprod (Representation.free k G α)).Coinvariants) diff --git a/Mathlib/RepresentationTheory/Continuous/Basic.lean b/Mathlib/RepresentationTheory/Continuous/Basic.lean index 506eb63ce991a7..81d7d2a93610de 100644 --- a/Mathlib/RepresentationTheory/Continuous/Basic.lean +++ b/Mathlib/RepresentationTheory/Continuous/Basic.lean @@ -29,6 +29,14 @@ related basic results. * `ContRepresentation.coind₁ π` is the coinduced continuous representation on the space of continuous functions from `G` to `V` for a continuous representation `π`. +* `ContIntertwiningMap.mapInvariantsOfRes φ f` is the continuous linear map + `π.invariants →L[R] π'.invariants` induced by a monoid homomorphism `φ : H →* G` and a + continuous intertwining map `f : π.restrict φ →ⁱL π'`. + +* `ContRepresentation.coind₁ResMap φ f` is the continuous intertwining map + `π.coind₁.restrict φ →ⁱL π'.coind₁` induced by a continuous group homomorphism `φ : H →ₜ* G` + and a continuous intertwining map `f : π.restrict φ →ⁱL π'`, given by `F ↦ f ∘ F ∘ φ`. + ## Tags continuous representation, algebra -/ @@ -42,12 +50,26 @@ variable (R G V W U : Type*) [Monoid G] [Ring R] [AddCommGroup V] [TopologicalSp /-- A continuous representation of a group `G` on a `R`-module `V` which is a topological addgroup is a homomorphism `G →* V →L[R] V`. -/ -abbrev ContRepresentation := G →* V →L[R] V +structure ContRepresentation where + ofMonoidHom :: + /-- The underlying monoid homomorphism of a continuous representation. -/ + toMonoidHom : G →* V →L[R] V + +instance : FunLike (ContRepresentation R G V) G (V →L[R] V) where + coe π := π.toMonoidHom + coe_injective π₁ π₂ _ := by cases π₁; cases π₂; simp_all + +instance : MonoidHomClass (ContRepresentation R G V) G (V →L[R] V) where + map_one π := π.toMonoidHom.map_one + map_mul π := π.toMonoidHom.map_mul + +lemma ContRepresentation.toMonoidHom_apply (π : ContRepresentation R G V) (g : G) : + π.toMonoidHom g = π g := rfl /-- Every continuous representation "is" a representation. -/ abbrev ContRepresentation.toRepresentation (π : ContRepresentation R G V) : Representation R G V := - .comp ContinuousLinearMap.toLinearMapRingHom.toMonoidHom π + .comp ContinuousLinearMap.toLinearMapRingHom.toMonoidHom π.toMonoidHom variable {R G V W U} @@ -66,6 +88,7 @@ namespace ContIntertwiningMap open ContRepresentation variable {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} + {π₃ : ContRepresentation R G U} /-- Any continuous intertwining map is an intertwining map. -/ abbrev toIntertwiningMap (f : π₁ →ⁱL π₂) : @@ -78,16 +101,25 @@ def id : π₁ →ⁱL π₁ where __ := ContinuousLinearMap.id R V isIntertwining' g := by simp +@[simp] +lemma toContinuousLinearMap_id : + (id : π₁ →ⁱL π₁).toContinuousLinearMap = ContinuousLinearMap.id R V := rfl + @[ext] lemma ext {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} {f g : π₁ →ⁱL π₂} (h : f.toContinuousLinearMap = g.toContinuousLinearMap) : f = g := by cases f; cases g; congr -lemma toIntertwiningMap_injective {π₁ : ContRepresentation R G V} +lemma toContinuousLinearMap_injective {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} : Function.Injective fun f : π₁ →ⁱL π₂ ↦ f.toContinuousLinearMap := fun _ _ ↦ ext +lemma toIntertwiningMap_injective {π₁ : ContRepresentation R G V} + {π₂ : ContRepresentation R G W} : + Function.Injective fun f : π₁ →ⁱL π₂ ↦ f.toIntertwiningMap := + fun _ _ _ ↦ by ext; simp_all + lemma toFun_injective {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} : Function.Injective fun f : π₁ →ⁱL π₂ ↦ f.toFun := fun f g h ↦ by ext x; exact congr_fun h x @@ -97,6 +129,11 @@ instance {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} : coe f := f.toFun coe_injective := toFun_injective +lemma id_apply (v : V) : (.id : π₁ →ⁱL π₁) v = v := rfl + +lemma toContinuousLinearMap_apply (f : π₁ →ⁱL π₂) (v : V) : + f.toContinuousLinearMap v = f v := rfl + lemma isIntertwining {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} (f : π₁ →ⁱL π₂) (g : G) (v : V) : f (π₁ g v) = π₂ g (f v) := f.toIntertwiningMap.isIntertwining _ _ g v @@ -114,6 +151,124 @@ def comp {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} __ := f.toContinuousLinearMap.comp g.toContinuousLinearMap isIntertwining' h := by rw [comp_assoc, g.2, ← comp_assoc, f.2, comp_assoc] +@[simp] +lemma toContinuousLinearMap_comp {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} + {π₃ : ContRepresentation R G U} (f : π₂ →ⁱL π₃) (g : π₁ →ⁱL π₂) : + (f.comp g).toContinuousLinearMap = f.toContinuousLinearMap.comp g.toContinuousLinearMap := rfl + +instance : Add (π₁ →ⁱL π₂) where + add f g := ⟨f.toContinuousLinearMap + g.toContinuousLinearMap, by simp [g.2, f.2]⟩ + +@[simp] +lemma toContinuousLinearMap_add (f g : π₁ →ⁱL π₂) : + (f + g).toContinuousLinearMap = f.toContinuousLinearMap + g.toContinuousLinearMap := rfl + +lemma add_apply (f g : π₁ →ⁱL π₂) (v : V) : (f + g) v = f v + g v := rfl + +lemma comp_add (f : π₂ →ⁱL π₃) (g h : π₁ →ⁱL π₂) : + f.comp (g + h) = f.comp g + f.comp h := by ext; simp + +lemma add_comp (f g : π₂ →ⁱL π₃) (h : π₁ →ⁱL π₂) : + (f + g).comp h = f.comp h + g.comp h := by ext; simp + +instance : One (π₁ →ⁱL π₁) where one := .id + +lemma one_def : (1 : π₁ →ⁱL π₁) = .id := rfl + +@[simp] +lemma toContinuousLinearMap_one : (1 : π₁ →ⁱL π₁).toContinuousLinearMap = 1 := rfl + +lemma one_apply (v : V) : (1 : π₁ →ⁱL π₁) v = v := rfl + +instance : Zero (π₁ →ⁱL π₂) where zero := ⟨0, by simp⟩ + +@[simp] +lemma toContinuousLinearMap_zero : (0 : π₁ →ⁱL π₂).toContinuousLinearMap = 0 := rfl + +lemma zero_apply (v : V) : (0 : π₁ →ⁱL π₂) v = 0 := rfl + +instance : AddZeroClass (π₁ →ⁱL π₂) := + fast_instance% toContinuousLinearMap_injective.addZeroClass _ + toContinuousLinearMap_zero toContinuousLinearMap_add + +instance : AddCommSemigroup (π₁ →ⁱL π₂) := + fast_instance% toContinuousLinearMap_injective.addCommSemigroup _ + toContinuousLinearMap_add + +instance : Neg (π₁ →ⁱL π₂) where + neg f := ⟨-f.toContinuousLinearMap, by simp [f.2]⟩ + +@[simp] +lemma toContinuousLinearMap_neg (f : π₁ →ⁱL π₂) : + (-f).toContinuousLinearMap = -f.toContinuousLinearMap := rfl + +lemma neg_apply (f : π₁ →ⁱL π₂) (v : V) : (-f) v = -f v := rfl + +instance : Sub (π₁ →ⁱL π₂) where + sub f g := ⟨f.toContinuousLinearMap - g.toContinuousLinearMap, by simp [g.2, f.2]⟩ + +@[simp] +lemma toContinuousLinearMap_sub (f g : π₁ →ⁱL π₂) : + (f - g).toContinuousLinearMap = f.toContinuousLinearMap - g.toContinuousLinearMap := rfl + +lemma sub_apply (f g : π₁ →ⁱL π₂) (v : V) : (f - g) v = f v - g v := rfl + +lemma sub_comp (f g : π₂ →ⁱL π₃) (h : π₁ →ⁱL π₂) : + (f - g).comp h = f.comp h - g.comp h := by + ext; simp + +lemma comp_sub (f : π₂ →ⁱL π₃) (g h : π₁ →ⁱL π₂) : + f.comp (g - h) = f.comp g - f.comp h := by + ext; simp + +instance instSMul {S : Type*} [Monoid S] [DistribMulAction S W] [SMulCommClass R S W] + [ContinuousConstSMul S W] [LinearMap.CompatibleSMul W W S R] : + SMul S (π₁ →ⁱL π₂) where + smul s f := ⟨s • f.toContinuousLinearMap, fun g ↦ by + rw [ContinuousLinearMap.smul_comp, f.2, ContinuousLinearMap.comp_smul]⟩ + +section addcommgroup + +variable {S : Type*} [Monoid S] [DistribMulAction S W] [SMulCommClass R S W] + [ContinuousConstSMul S W] [LinearMap.CompatibleSMul W W S R] + +@[simp] +lemma toContinuousLinearMap_smul (s : S) (f : π₁ →ⁱL π₂) : + (s • f).toContinuousLinearMap = s • f.toContinuousLinearMap := rfl + +lemma smul_apply (s : S) (f : π₁ →ⁱL π₂) (v : V) : (s • f) v = s • f v := rfl + +lemma smul_comp {S : Type*} [Monoid S] [DistribMulAction S U] [SMulCommClass R S U] + [ContinuousConstSMul S U] [LinearMap.CompatibleSMul U U S R] + (s : S) (f : π₂ →ⁱL π₃) (g : π₁ →ⁱL π₂) : (s • f).comp g = s • (f.comp g) := by + ext; simp + +lemma comp_smul {S : Type*} [Monoid S] [DistribMulAction S U] [SMulCommClass R S U] + [ContinuousConstSMul S U] [LinearMap.CompatibleSMul U U S R] + [DistribMulAction S W] [SMulCommClass R S W] [ContinuousConstSMul S W] + [LinearMap.CompatibleSMul W W S R] [LinearMap.CompatibleSMul W U S R] + (s : S) (f : π₂ →ⁱL π₃) (g : π₁ →ⁱL π₂) : f.comp (s • g) = s • (f.comp g) := by + ext; simp + +instance : AddCommGroup (π₁ →ⁱL π₂) := + fast_instance% toContinuousLinearMap_injective.addCommGroup _ toContinuousLinearMap_zero + toContinuousLinearMap_add toContinuousLinearMap_neg toContinuousLinearMap_sub + (fun _ _ ↦ toContinuousLinearMap_smul _ _) (fun _ _ ↦ toContinuousLinearMap_smul _ _) + +instance : DistribMulAction S (π₁ →ⁱL π₂) where + one_smul _ := by ext; simp + mul_smul _ _ _ := by ext; simp [mul_smul] + smul_zero _ := by ext; simp + smul_add _ _ _ := by ext; simp [smul_add] + +instance instModule {S : Type*} [Ring S] [Module S W] [SMulCommClass R S W] + [ContinuousConstSMul S W] [LinearMap.CompatibleSMul W W S R] : + Module S (π₁ →ⁱL π₂) where + add_smul _ _ _ := by ext; simp [add_smul] + zero_smul _ := by ext; simp + +end addcommgroup + end ContIntertwiningMap namespace ContRepresentation @@ -269,15 +424,99 @@ end Equiv variable (R G V) in /-- The trivial continuous representation of a group `G` on a `R`-module `V`. -/ -def trivial : ContRepresentation R G V := 1 +def trivial : ContRepresentation R G V := ofMonoidHom 1 @[simp] lemma trivial_apply (g : G) (v : V) : trivial R G V g v = v := rfl /-- The restriction of a continuous representation along a monoid homomorphism. -/ -@[simps!] +@[implicit_reducible] def restrict {H : Type*} [Monoid H] (π : ContRepresentation R G V) (φ : H →* G) : - ContRepresentation R H V := .comp π φ + ContRepresentation R H V := ofMonoidHom (π.toMonoidHom.comp φ) + +lemma restrict_apply {H : Type*} [Monoid H] (π : ContRepresentation R G V) (φ : H →* G) + (h : H) : π.restrict φ h = π (φ h) := rfl + +@[simp] +lemma restrict_apply_apply {H : Type*} [Monoid H] (π : ContRepresentation R G V) (φ : H →* G) + (h : H) (v : V) : π.restrict φ h v = π (φ h) v := rfl + +/-- The restriction of a continuous intertwining map along a monoid homomorphism. -/ +def _root_.ContIntertwiningMap.restrict {H : Type*} [Monoid H] {π : ContRepresentation R G V} + {π' : ContRepresentation R G W} (φ : H →* G) (f : π →ⁱL π') : + π.restrict φ →ⁱL π'.restrict φ where + __ := f.toContinuousLinearMap + isIntertwining' h := by + ext; simp [f.toContinuousLinearMap_apply, f.isIntertwining] + +lemma _root_.ContIntertwiningMap.restrict_toContinuousLinearMap {H : Type*} [Monoid H] + {π : ContRepresentation R G V} {π' : ContRepresentation R G W} (φ : H →* G) (f : π →ⁱL π') : + (f.restrict φ).toContinuousLinearMap = f.toContinuousLinearMap := rfl + +@[simp] lemma _root_.ContIntertwiningMap.restrict_apply {H : Type*} [Monoid H] + {π : ContRepresentation R G V} {π' : ContRepresentation R G W} (φ : H →* G) + (f : π →ⁱL π') (v : V) : f.restrict φ v = f v := rfl + +lemma _root_.ContIntertwiningMap.restrict_sub {H : Type*} [Monoid H] + {π : ContRepresentation R G V} {π' : ContRepresentation R G W} (φ : H →* G) + (f g : π →ⁱL π') : (f - g).restrict φ = f.restrict φ - g.restrict φ := rfl + +/-- The submodule of `G`-invariant elements of a continuous representation. -/ +def invariants (π : ContRepresentation R G V) : Submodule R V where + carrier := {v | ∀ g, π g v = v} + zero_mem' := by simp + add_mem' _ _ := by simp_all + smul_mem' _ _ hv g := by simp [hv g] + +@[simp] +lemma mem_invariants {π : ContRepresentation R G V} (v : V) : + v ∈ π.invariants ↔ ∀ g, π g v = v := Iff.rfl + +/-- The map induced on `G`-invariant elements by a continuous intertwining map. -/ +def _root_.ContIntertwiningMap.mapInvariants + {π : ContRepresentation R G V} {π' : ContRepresentation R G W} + (f : π →ⁱL π') : π.invariants →L[R] π'.invariants := + f.toContinuousLinearMap.restrict <| by + simp +contextual [f.toContinuousLinearMap_apply, ← f.isIntertwining] + +-- provided for rewrite, this lemma should be used when `mapInvariants` is +-- applied to `(homogeneousCochains X).X 0` +lemma _root_.ContIntertwiningMap.mapInvariants_apply + {π : ContRepresentation R G V} {π' : ContRepresentation R G W} + (f : π →ⁱL π') (v : π.invariants) : + f.mapInvariants v = f v := rfl + +@[simp] +lemma _root_.ContIntertwiningMap.mk_mapInvariants_apply + {π : ContRepresentation R G V} {π' : ContRepresentation R G W} + (f : π →ⁱL π') (v : V) (hv : v ∈ π.invariants) : + f.mapInvariants ⟨v, hv⟩ = f v := rfl + +variable {H : Type*} [Monoid H] + +lemma invariants_le_invariants_restrict (π : ContRepresentation R G V) (φ : H →* G) : + π.invariants ≤ (π.restrict φ).invariants := + fun _ hv h ↦ hv (φ h) + +variable {π : ContRepresentation R G V} {π' : ContRepresentation R H W} + +/-- Given a monoid homomorphism `φ : H →* G`, a `G`-representation `π` and an +`H`-representation `π'`, a continuous intertwining map `f : π.restrict φ →ⁱL π'` induces a +continuous linear map `π.invariants →L[R] π'.invariants` between the invariant submodules. -/ +def _root_.ContIntertwiningMap.mapInvariantsOfRes (φ : H →* G) + (f : π.restrict φ →ⁱL π') : π.invariants →L[R] π'.invariants := + f.toContinuousLinearMap.restrict <| by + simp +contextual [f.toContinuousLinearMap_apply, ← f.isIntertwining] + +-- provided for rewriting +lemma _root_.ContIntertwiningMap.mapInvariantsOfRes_apply (φ : H →* G) + (f : π.restrict φ →ⁱL π') (v : π.invariants) : + f.mapInvariantsOfRes φ v = f v := rfl + +@[simp] +lemma _root_.ContIntertwiningMap.mk_mapInvariantsOfRes_apply (φ : H →* G) + (f : π.restrict φ →ⁱL π') (v : V) (hv : v ∈ π.invariants) : + f.mapInvariantsOfRes φ ⟨v, hv⟩ = f v := rfl -- TODO : define `IsTopologicalMonoid` and then replace `Homeomorph.mulLeft g⁻¹` with the -- `ContinuousMap.mulRight g` to make `coind₁` work for monoids. @@ -299,20 +538,20 @@ lemma mem_coindV (f : C(H, V)) : f ∈ π.coindV φ ↔ ∀ g h, f (φ g * h) = instance : ContinuousSMul R (π.coindV φ) where continuous_smul := by continuity -variable [IsTopologicalRing R] [IsTopologicalGroup G] [IsTopologicalGroup H] +variable [IsTopologicalGroup G] [IsTopologicalGroup H] /-- The coinduced continuous representation where the action of `H` is defined by `h ↦ f ↦ f ∘ (· * h)`. -/ @[simps] def coind (π : ContRepresentation R G V) : ContRepresentation R H (π.coindV φ) where - toFun h := { + toMonoidHom.toFun h := { toFun | ⟨f, hf⟩ => ⟨f.comp (ContinuousMap.mulRight h), by simp [mul_assoc, hf _]⟩ map_add' _ _ := by simp map_smul' _ _ := by simp cont := continuous_induced_rng.2 <| by simpa using! (ContinuousMap.mulRight h).continuous_precomp.comp continuous_subtype_val} - map_one' := by ext; simp - map_mul' h1 h2 := by ext; simp [ContinuousMap.mulRight_mul] + toMonoidHom.map_one' := by ext; simp + toMonoidHom.map_mul' h1 h2 := by ext; simp [ContinuousMap.mulRight_mul] open ContinuousMap @@ -322,21 +561,24 @@ The action of an element `g : G` is defined by `f ↦ (x ↦ π g (f (g⁻¹ * x This new representation of `G` is isomorphic to the continuous coinduction of the trivial representation of the trivial subgroup of `G`, but the action has been twisted so that the map `const : V → C(G,V)` is an intertwining map. -/ -@[simps] def coind₁ (π : ContRepresentation R G V) : ContRepresentation R G C(G, V) where - toFun g := { + toMonoidHom.toFun g := { toFun f := .comp (π g) (f.comp (ContinuousMap.mulLeft g⁻¹)) map_add' _ _ := by ext; simp map_smul' _ _ := by ext; simp cont := (continuous_postcomp _).comp (continuous_precomp _) } - map_one' := by ext; simp - map_mul' _ _ := by ext; simp [mul_assoc] + toMonoidHom.map_one' := by ext; simp + toMonoidHom.map_mul' _ _ := by ext; simp [mul_assoc] + +@[simp] +lemma coind₁_apply_apply (π : ContRepresentation R G V) (g : G) (f : C(G, V)) (x : G) : + π.coind₁ g f x = π g (f (g⁻¹ * x)) := rfl /-- The functoriality of `coind₁`. -/ @[simps] -def coind₁_map (π₁ : ContRepresentation R G V) (π₂ : ContRepresentation R G W) (f : π₁ →ⁱL π₂) : +def coind₁Map {π₁ : ContRepresentation R G V} {π₂ : ContRepresentation R G W} (f : π₁ →ⁱL π₂) : coind₁ π₁ →ⁱL coind₁ π₂ where toFun := (f : ContinuousMap _ _).comp map_add' _ _ := by ext; simp @@ -346,8 +588,8 @@ def coind₁_map (π₁ : ContRepresentation R G V) (π₂ : ContRepresentation /-- The naturality of the transformation from `𝟭 ⟶ coind₁`. -/ @[simps] -def coind₁_ι (π : ContRepresentation R G V) : π →ⁱL coind₁ π where - toFun := .const G +def coind₁ι (π : ContRepresentation R G V) : π →ⁱL coind₁ π where + toFun := ContinuousMap.const G map_add' _ _ := rfl map_smul' _ _ := rfl isIntertwining' := by aesop @@ -360,4 +602,47 @@ def coind₁Equivcoind : (coind₁ (.trivial R (⊥ : Subgroup G) V)).Equiv ContinuousLinearEquiv.ofEq _ _ (by simp [SetLike.ext_iff])) <| fun g ↦ by simp [Subsingleton.elim g 1, ContinuousLinearMap.one_def] +section coind₁ResMap + +variable [ContinuousSMul R U] {π' : ContRepresentation R H W} {π} + +/-- Given a continuous group homomorphism `φ : H →ₜ* G`, precomposition with `φ` defines a +continuous intertwining map `π.coind₁.restrict φ →ⁱL (π.restrict φ).coind₁`. -/ +def coind₁Res (φ : H →ₜ* G) (π : ContRepresentation R G V) : + π.coind₁.restrict (φ : H →* G) →ⁱL (π.restrict (φ : H →* G)).coind₁ where + __ := ContinuousMap.compCLM R V φ.toContinuousMap + isIntertwining' h := by + ext F x + simp [map_mul, map_inv] + +@[simp] +lemma coind₁Res_apply (φ : H →ₜ* G) (π : ContRepresentation R G V) (F : C(G, V)) (x : H) : + coind₁Res φ π F x = F (φ x) := rfl + +/-- Given a continuous group homomorphism `φ : H →ₜ* G`, a continuous intertwining map +`f : π.restrict φ →ⁱL π'` induces a continuous intertwining map +`π.coind₁.restrict φ →ⁱL π'.coind₁`, sending `F : C(G, V)` to `f ∘ F ∘ φ : C(H, W)`. -/ +def coind₁ResMap (φ : H →ₜ* G) (f : π.restrict (φ : H →* G) →ⁱL π') : + π.coind₁.restrict (φ : H →* G) →ⁱL π'.coind₁ := + (coind₁Map f).comp (coind₁Res φ π) + +@[simp] +lemma coind₁ResMap_apply (φ : H →ₜ* G) (f : π.restrict (φ : H →* G) →ⁱL π') (F : C(G, V)) + (x : H) : coind₁ResMap φ f F x = f (F (φ x)) := rfl + +/-- The naturality of `coind₁ι` with respect to `coind₁ResMap`. -/ +lemma coind₁ResMap_comp_coind₁ι_restrict (φ : H →ₜ* G) (f : π.restrict (φ : H →* G) →ⁱL π') : + (coind₁ResMap φ f).comp (π.coind₁ι.restrict (φ : H →* G)) = π'.coind₁ι.comp f := rfl + +lemma coind₁Map_comp_coind₁ResMap (φ : H →ₜ* G) {σ : ContRepresentation R H U} + (f : π.restrict φ →ⁱL π') (g : π' →ⁱL σ) : + (coind₁Map g).comp (coind₁ResMap φ f) = coind₁ResMap φ (g.comp f) := rfl + +lemma coind₁ResMap_comp_coind₁Map_restrict (φ : H →ₜ* G) {ρ : ContRepresentation R G U} + (g : ρ →ⁱL π) (f : π.restrict (φ : H →* G) →ⁱL π') : + (coind₁ResMap φ f).comp ((coind₁Map g).restrict (φ : H →* G)) = + coind₁ResMap φ (f.comp (g.restrict (φ : H →* G))) := rfl + +end coind₁ResMap + end ContRepresentation diff --git a/Mathlib/RepresentationTheory/Continuous/TopRep.lean b/Mathlib/RepresentationTheory/Continuous/TopRep.lean index 1240e47542d0a2..54963a10aee64a 100644 --- a/Mathlib/RepresentationTheory/Continuous/TopRep.lean +++ b/Mathlib/RepresentationTheory/Continuous/TopRep.lean @@ -6,6 +6,7 @@ Authors: Edison Xie, Richard Hill module public import Mathlib.CategoryTheory.Action.Basic +public import Mathlib.CategoryTheory.Linear.LinearFunctor public import Mathlib.RepresentationTheory.Continuous.Basic /-! @@ -13,6 +14,11 @@ public import Mathlib.RepresentationTheory.Continuous.Basic This file defines the category `TopRep k G` of topological representations of a monoid `G` over a topological ring `k`, and shows that it is equivalent to the category `Action (TopModuleCat k) G`. + +For a topological group `G` we define the invariants functor `TopRep.invariantsFunctor`, the +coinduction functor `TopRep.coind₁Functor`, the restriction functor `TopRep.resFunctor` along a +group homomorphism `φ : H →* G`, and the morphism `TopRep.invariantsResMap φ f` between invariant +submodules induced by a morphism `f : res φ X ⟶ Y`. -/ @[expose] public section @@ -21,8 +27,7 @@ universe w u v /-- The category of topological representations of a monoid `G` over a topological ring `k`, and their morphisms. -/ -structure TopRep (k : Type u) (G : Type v) [TopologicalSpace k] [Ring k] - [IsTopologicalRing k] [Monoid G] where +structure TopRep (k : Type u) (G : Type v) [Ring k] [TopologicalSpace k] [Monoid G] where private mk :: /-- the underlying type of an object in `TopRep k G` -/ V : Type w @@ -37,7 +42,7 @@ structure TopRep (k : Type u) (G : Type v) [TopologicalSpace k] [Ring k] namespace TopRep variable {k : Type u} {G : Type v} {X Y : Type w} [TopologicalSpace k] [Ring k] - [IsTopologicalRing k] [Monoid G] [AddCommGroup X] [Module k X] [TopologicalSpace X] + [Monoid G] [AddCommGroup X] [Module k X] [TopologicalSpace X] [IsTopologicalAddGroup X] [ContinuousSMul k X] [AddCommGroup Y] [Module k Y] [TopologicalSpace Y] [IsTopologicalAddGroup Y] [ContinuousSMul k Y] {ρ : ContRepresentation k G X} {σ : ContRepresentation k G Y} @@ -128,6 +133,58 @@ variable {A B} in lemma hom_comm_apply (f : A ⟶ B) (g : G) (a : A) : f.hom (A.ρ g a) = B.ρ g (f.hom a) := by simpa using! congr($(f.hom.2 g) a) +instance : AddCommGroup (A ⟶ B) := fast_instance% ConcreteCategory.homEquiv.addCommGroup + +@[simp] lemma hom_zero : (0 : A ⟶ B).hom = 0 := rfl + +lemma hom_add (f g : A ⟶ B) : (f + g).hom = f.hom + g.hom := rfl + +lemma hom_sub (f g : A ⟶ B) : (f - g).hom = f.hom - g.hom := rfl + +lemma ofHom_add (f g : ρ →ⁱL σ) : ofHom (f + g) = ofHom f + ofHom g := rfl + +lemma ofHom_sub (f g : ρ →ⁱL σ) : ofHom (f - g) = ofHom f - ofHom g := rfl + +lemma comp_add' (f : A ⟶ B) (g h : B ⟶ C) : f ≫ (g + h) = f ≫ g + f ≫ h := by + ext : 1; simp [hom_add, ContIntertwiningMap.add_comp] + +lemma add_comp' (f g : A ⟶ B) (h : B ⟶ C) : (f + g) ≫ h = f ≫ h + g ≫ h := by + ext : 1; simp [hom_add, ContIntertwiningMap.comp_add] + +instance : Preadditive (TopRep k G) where + homGroup := inferInstance + add_comp := TopRep.add_comp' + comp_add := TopRep.comp_add' + +section Linear + +variable {k : Type u} {G : Type v} {X Y : Type w} [TopologicalSpace k] [CommRing k] + [Monoid G] [AddCommGroup X] [Module k X] [TopologicalSpace X] + [IsTopologicalAddGroup X] [ContinuousSMul k X] [AddCommGroup Y] [Module k Y] [TopologicalSpace Y] + [IsTopologicalAddGroup Y] [ContinuousSMul k Y] {ρ : ContRepresentation k G X} + {σ : ContRepresentation k G Y} {A B C : TopRep k G} + +instance : Module k (A ⟶ B) := fast_instance% ConcreteCategory.homEquiv.module k + +lemma hom_smul (r : k) (f : A ⟶ B) : (r • f).hom = r • f.hom := rfl + +lemma ofHom_smul (r : k) (f : ρ →ⁱL σ) : ofHom (r • f) = r • ofHom f := rfl + +variable (A B C) in +lemma smul_comp' (r : k) (f : A ⟶ B) (g : B ⟶ C) : (r • f) ≫ g = r • (f ≫ g) := by + ext; simp [hom_smul, ContIntertwiningMap.comp_smul] + +variable (A B C) in +lemma comp_smul' (f : A ⟶ B) (r : k) (g : B ⟶ C) : f ≫ (r • g) = r • (f ≫ g) := by + ext; simp [hom_smul, ContIntertwiningMap.smul_comp] + +instance : CategoryTheory.Linear k (TopRep k G) where + homModule := inferInstance + smul_comp := smul_comp' + comp_smul := comp_smul' + +end Linear + section equivAction /-- The functor sending a topological representation to the corresponding object in @@ -139,8 +196,9 @@ def toActionTopModFunc : TopRep k G ⥤ Action (TopModuleCat k) G where /-- The functor sending an object in `Action (TopModuleCat k) G` to the corresponding topological representation. -/ def fromActionTopModFunc : Action (TopModuleCat.{w} k) G ⥤ TopRep k G where - obj X := .of <| (TopModuleCat.endRingEquiv X.V).toMonoidHom.comp X.ρ - map {X Y} f := ofHom ⟨f.hom.hom, fun g ↦ by simpa using congr(TopModuleCat.Hom.hom $(f.comm g))⟩ + obj X := .of <| .ofMonoidHom <| (TopModuleCat.endRingEquiv X.V).toMonoidHom.comp X.ρ + map {X Y} f := ofHom ⟨f.hom.hom, fun g ↦ by + simpa [← toMonoidHom_apply] using congr(TopModuleCat.Hom.hom $(f.comm g))⟩ /-- The unit isomorphism of the equivalence `TopRepIsoActionTop`. -/ def toActionFromAction (X : TopRep.{w} k G) : @@ -169,4 +227,75 @@ instance : (fromActionTopModFunc (k := k) (G := G)).IsEquivalence := end equivAction +variable {G : Type v} [Group G] [TopologicalSpace G] [IsTopologicalGroup G] + +/-- The `G`-invariant topologicalsubmodule of a topological representation. -/ +abbrev invariants (X : TopRep k G) : TopModuleCat k := .of k X.ρ.invariants + +variable (k G) in +/-- The functor taking an `R`-linear `G`-representation to its `G`-invariant submodule. -/ +abbrev invariantsFunctor : TopRep k G ⥤ TopModuleCat k where + obj A := .of k A.ρ.invariants + map f := TopModuleCat.ofHom f.hom.mapInvariants + +instance : (invariantsFunctor k G).Additive where + +instance {k : Type u} [CommRing k] [TopologicalSpace k] : (invariantsFunctor k G).Linear k where + +/-- The top rep induced by the coinduced representation. -/ +abbrev coind₁ (A : TopRep k G) : TopRep k G := of A.ρ.coind₁ + +variable (k G) in +/-- The functor taking a representation `rep` to the representation `C(G, rep)`. +The `G` action is defined by `g • f := x ↦ g • f (g⁻¹ * x)`. -/ +abbrev coind₁Functor : TopRep k G ⥤ TopRep k G where + obj := coind₁ + map φ := ofHom <| ContRepresentation.coind₁Map φ.hom + +instance : (TopRep.coind₁Functor k G).Additive where + +instance {k : Type u} [CommRing k] [TopologicalSpace k] : (coind₁Functor k G).Linear k where + +/-- The constant function `rep ⟶ C(G, rep)` as a natural transformation. -/ +@[implicit_reducible, simps] +def coind₁ι : 𝟭 (TopRep k G) ⟶ coind₁Functor k G where + app rep := ofHom rep.ρ.coind₁ι + +/-- The restriction of a topological representation along a monoid homomorphism. -/ +abbrev res {H : Type*} [Monoid H] (φ : H →* G) (A : TopRep k G) : TopRep k H := of (A.ρ.restrict φ) + +/-- The functor taking a topological `G`-representation to a topological `H`-representation +along a monoid homomorphism `φ : H →* G`. -/ +abbrev resFunctor {H : Type*} [Monoid H] (φ : H →* G) : + TopRep k G ⥤ TopRep k H where + obj := res φ + map f := ofHom <| f.hom.restrict φ + +section invariantsResMap + +variable {G H : Type*} [Group G] + +@[simp] +lemma resFunctor_map_hom [Monoid H] (φ : H →* G) {A B : TopRep k G} (f : A ⟶ B) : + ((resFunctor φ).map f).hom = f.hom.restrict φ := rfl + +variable [Group H] + +/-- The morphism between invariant submodules induced by a morphism `res φ X ⟶ Y` of +topological `H`-representations, where `φ : H →* G` is a group homomorphism. -/ +def invariantsResMap (φ : H →* G) {X : TopRep k G} {Y : TopRep k H} (f : res φ X ⟶ Y) : + X.invariants ⟶ Y.invariants := + TopModuleCat.ofHom (f.hom.mapInvariantsOfRes φ) + +lemma invariantsResMap_comp {X : TopRep k G} {Y Y' : TopRep k H} (φ : H →* G) + (f : res φ X ⟶ Y) (g : Y ⟶ Y') : + invariantsResMap φ (f ≫ g) = invariantsResMap φ f ≫ (invariantsFunctor k H).map g := rfl + +lemma invariantsResMap_map_comp {X X' : TopRep k G} {Y : TopRep k H} (φ : H →* G) + (f : X ⟶ X') (g : res φ X' ⟶ Y) : + invariantsResMap φ ((resFunctor φ).map f ≫ g) = + (invariantsFunctor k G).map f ≫ invariantsResMap φ g := rfl + +end invariantsResMap + end TopRep diff --git a/Mathlib/RepresentationTheory/FinGroupCharZero.lean b/Mathlib/RepresentationTheory/FinGroupCharZero.lean index 22633b0e48855b..6191fee0b4a04b 100644 --- a/Mathlib/RepresentationTheory/FinGroupCharZero.lean +++ b/Mathlib/RepresentationTheory/FinGroupCharZero.lean @@ -119,30 +119,12 @@ If `G` is finite and `k` an algebraically closed field of characteristic `0`, then an object of `FDRep k G` is simple if and only if its character has norm `1`. -/ lemma simple_iff_char_is_norm_one [CharZero k] [Fintype G] (V : FDRep k G) : - Simple V ↔ ∑ g : G, V.character g * V.character g⁻¹ = Nat.card G where - mp h := by - have : NeZero (Nat.card G : k) := by - rw [← @Fintype.card_eq_nat_card G (by assumption)] - exact NeZero.charZero - have := invertibleOfNonzero (NeZero.ne (Nat.card G : k)) - have := invertibleOfNonzero (NeZero.ne (Fintype.card G : k)) - classical - have : ⅟(Nat.card G : k) • ∑ g, V.character g * V.character g⁻¹ = 1 := by - simpa only [Nonempty.intro (Iso.refl V), ↓reduceIte, Fintype.card_eq_nat_card] - using char_orthonormal V V - apply_fun (· * (Fintype.card G : k)) at this - rwa [mul_comm, ← smul_eq_mul, smul_smul, Fintype.card_eq_nat_card, mul_invOf_self, smul_eq_mul, - one_mul, one_mul] at this - mpr h := by - have : NeZero (Nat.card G : k) := by - rw [← @Fintype.card_eq_nat_card G (by assumption)] - exact NeZero.charZero - have := invertibleOfNonzero (NeZero.ne (Fintype.card G : k)) - have := invertibleOfNonzero (NeZero.ne (Nat.card G : k)) - have eq := FDRep.scalar_product_char_eq_finrank_equivariant V V - rw [h] at eq - simp only [invOf_eq_inv, smul_eq_mul, inv_mul_cancel_of_invertible, Fintype.card_eq_nat_card] - at eq + Simple V ↔ ∑ g : G, V.character g * V.character g⁻¹ = Nat.card G := by + have := invertibleOfNonzero (NeZero.ne (Nat.card G : k)) + constructor <;> intro h + · symm; simpa [Nonempty.intro (Iso.refl V), inv_mul_eq_one₀] using char_orthonormal V V + · have eq := V.scalar_product_char_eq_finrank_equivariant V + rw [h, inv_mul_cancel_of_invertible] at eq rw [simple_iff_end_is_rank_one, ← Nat.cast_inj (R := k), ← eq, Nat.cast_one] end FDRep diff --git a/Mathlib/RepresentationTheory/FiniteIndex.lean b/Mathlib/RepresentationTheory/FiniteIndex.lean index 8e0ccd7172bf78..49c52f3cd7e9cd 100644 --- a/Mathlib/RepresentationTheory/FiniteIndex.lean +++ b/Mathlib/RepresentationTheory/FiniteIndex.lean @@ -177,10 +177,9 @@ noncomputable def indCoindIso (A : Rep.{max w u} k S) : variable (k S) -set_option backward.defeqAttrib.useBackward true in /-- Given a finite index subgroup `S ≤ G`, this is a natural isomorphism between the `Ind_S^G` and `Coind_G^S` functors `Rep k S ⥤ Rep k G`. -/ -@[simps! hom_app inv_app] +@[implicit_reducible, simps! hom_app inv_app] noncomputable def indCoindNatIso : indFunctor k S.subtype ≅ coindFunctor.{max w u} k S.subtype := NatIso.ofComponents (fun (A : Rep k S) => indCoindIso A) fun f => by @@ -194,7 +193,6 @@ noncomputable def resIndAdjunction : resFunctor.{max w u v} S.subtype ⊣ indFunctor.{max w u v} k S.subtype := (resCoindAdjunction.{max w u v} k S.subtype).ofNatIsoRight (indCoindNatIso.{max w u v} k S).symm - omit [DecidableRel (QuotientGroup.rightRel S)] in @[instance] -- Note: we must use `@[instance] theorem` here due to [lean4#5595](https://github.com/leanprover/lean4/issues/5595). theorem instIsRightAdjointSubtypeMemSubgroupIndFunctorSubtype : @@ -215,8 +213,6 @@ lemma resIndAdjunction_unit_app (B : Rep.{max w u v} k G) : (resCoindAdjunction.{max w u} k S.subtype).unit.app B ≫ (indCoindIso.{max w (max u v)} (res S.subtype B)).inv := rfl -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in lemma resIndAdjunction_homEquiv_apply (A : Rep.{max w u v} k S) {B : Rep.{max w u v} k G} (f : res S.subtype B ⟶ A) : (resIndAdjunction.{w, u, v} k S).homEquiv _ _ f = @@ -228,7 +224,7 @@ lemma resIndAdjunction_homEquiv_symm_apply (A : Rep.{max w u v} k S) {B : Rep.{max w u v} k G} (f : B ⟶ (indFunctor k S.subtype).obj A) : ((resIndAdjunction k S).homEquiv _ _).symm f = - (resCoindHomEquiv.{max w u v} S.subtype B A).symm (f ≫ (indCoindIso.{max w u v} A).hom) := by + (resCoindHomEquiv.{max w u v} S.subtype B A).symm (f ≫ (indCoindIso.{max w u v} A).hom) := rfl variable (k S) in @@ -248,7 +244,7 @@ theorem instIsLeftAdjointSubtypeMemSubgroupCoindFunctorSubtype : lemma coindResAdjunction_counit_app (B : Rep.{max w u v} k G) : (coindResAdjunction.{w, u, v} k S).counit.app B = (indCoindIso.{max w u v} (res S.subtype B)).inv ≫ - (indResAdjunction k S.subtype).counit.app B := by + (indResAdjunction k S.subtype).counit.app B := rfl set_option backward.isDefEq.respectTransparency false in @@ -257,8 +253,7 @@ lemma coindResAdjunction_unit_app (A : Rep.{max w u v} k S) : (coindResAdjunction k S).unit.app A = (indResAdjunction k S.subtype).unit.app A ≫ (resFunctor S.subtype).map (indCoindIso.{max w u v} A).hom := by ext - simp [coindResAdjunction, Adjunction.ofNatIsoLeft, - indResAdjunction, indCoindIso] + simp [coindResAdjunction] lemma coindResAdjunction_homEquiv_apply (A : Rep.{max w u v} k S) {B : Rep k G} (f : coind S.subtype A ⟶ B) : @@ -270,9 +265,7 @@ lemma coindResAdjunction_homEquiv_symm_apply (A : Rep.{max w u v} k S) {B : Rep k G} (f : A ⟶ res S.subtype B) : ((coindResAdjunction.{max w u v} k S).homEquiv _ _).symm f = (indCoindIso.{max w u v} A).inv ≫ (indResHomEquiv S.subtype A B).symm f := by - simp only [coindResAdjunction, indResAdjunction, + simp [coindResAdjunction, indResHomEquiv, indResAdjunction, Adjunction.homEquiv_ofNatIsoLeft_symm_apply _] - simp - rfl end Rep diff --git a/Mathlib/RepresentationTheory/Homological/ContCohomology/Basic.lean b/Mathlib/RepresentationTheory/Homological/ContCohomology/Basic.lean new file mode 100644 index 00000000000000..c7fdc66d39ca92 --- /dev/null +++ b/Mathlib/RepresentationTheory/Homological/ContCohomology/Basic.lean @@ -0,0 +1,149 @@ +/- +Copyright (c) 2026 Richard Hill. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Richard Hill, Andrew Yang, Edison Xie +-/ + +module + +public import Mathlib.Algebra.Homology.ShortComplex.HomologicalComplex +public import Mathlib.Algebra.Category.ModuleCat.Topology.Homology +public import Mathlib.RepresentationTheory.Continuous.TopRep + +/-! + +# Continuous cohomology + +We define continuous cohomology as the homology of the homogeneous cochain complex. + +## Implementation details + +We define homogeneous cochains as `g`-invariant continuous function in `C(G, C(G,...,C(G, M)))` +instead of the usual `C(Gⁿ, M)` to allow more general topological groups other than locally compact +ones. For this to work, we also work in `TopRep k G`, where the `G` action on `M` +is only continuous on `M`, and not necessarily continuous in both variables, because the `G` action +on `C(G, M)` might not be continuous on both variables even if it is on `M`. + +For the differential map, instead of a finite sum we use the inductive definition +`d₋₁ : M → C(G, M) := const : m ↦ g ↦ m` and +`dₙ₊₁ : C(G, _) → C(G, C(G, _)) := const - C(G, dₙ) : f ↦ g ↦ f - dₙ (f (g))` +See `TopRep.d`. + +## Main definition +- `TopRep.homogeneousCochains`: + The functor taking an `R`-linear `G`-representation to the complex of homogeneous cochains. +- `continuousCohomology`: + The functor taking an `R`-linear `G`-representation to its `n`-th continuous cohomology. + +## TODO +- Show that it coincides with `groupCohomology` for discrete groups. +- Give the usual description of cochains in terms of `n`-ary functions for locally compact groups. +- Show that short exact sequences induce long exact sequences in certain scenarios. +-/ + +@[expose] public section + +variable {k G : Type*} [Ring k] [Group G] [TopologicalSpace k] + [TopologicalSpace G] [IsTopologicalGroup G] + +open CategoryTheory ContRepresentation Limits + +namespace TopRep + +/-- The `n`-th term in the resolution of a topological representation induced by `TopRep.coind₁`. -/ +abbrev resolutionX (X : TopRep k G) : ℕ → TopRep k G + | 0 => X + | n + 1 => (resolutionX X n).coind₁ + +/-- The boundary map in the resolution of a topological representation induced +by `TopRep.coind₁Functor`. -/ +def d (X : TopRep k G) : (n : ℕ) → resolutionX X n ⟶ resolutionX X (n + 1) + | 0 => ofHom X.ρ.coind₁ι + | n + 1 => ofHom (resolutionX X (n + 1)).ρ.coind₁ι - (coind₁Functor k G).map (d X n) + +lemma d_zero (X : TopRep k G) : d X 0 = ofHom X.ρ.coind₁ι := rfl + +lemma d_succ (X : TopRep k G) (n : ℕ) : + d X (n + 1) = ofHom (resolutionX X (n + 1)).ρ.coind₁ι - (coind₁Functor k G).map (d X n) := + rfl + +lemma hom_d_succ (X : TopRep k G) (n : ℕ) : + (d X (n + 1)).hom = (resolutionX X (n + 1)).ρ.coind₁ι - + ContRepresentation.coind₁Map (d X n).hom := + rfl + +@[reassoc (attr := simp)] +lemma d_comp_d (X : TopRep k G) (n : ℕ) : d X n ≫ d X (n + 1) = 0 := by + induction n with + | zero => + ext + simp [d_succ, ContIntertwiningMap.toContinuousLinearMap_apply, d_zero, hom_sub] + | succ n ih => + rw [d_succ _ (n + 1), Preadditive.comp_sub] + nth_rw 2 [d_succ] + rw [Preadditive.sub_comp, ← Functor.map_comp, ih, Functor.map_zero, sub_zero, sub_eq_zero] + rfl + +/-- The complex of functors whose behaviour pointwise takes an `R`-linear `G`-representation `M` +to the complex `M → C(G, M) → ⋯ → C(G, C(G,...,C(G, M))) → ⋯` +The `G`-invariant submodules of it is the homogeneous cochains (shifted by one). -/ +abbrev resolution (X : TopRep k G) : CochainComplex (TopRep k G) ℕ := + CochainComplex.of (resolutionX X) (d X) (d_comp_d X) + +/-- The shifted object in resolution by `1` degree. -/ +abbrev resolution'X (X : TopRep k G) (n : ℕ) : TopRep k G := resolutionX X (n + 1) + +/-- The shifted boundary map of the resolution. -/ +@[implicit_reducible] +def resolution'd (X : TopRep k G) (n : ℕ) : + resolution'X X n ⟶ resolution'X X (n + 1) := d X (n + 1) + +lemma resolution'd_eq (X : TopRep k G) (n : ℕ) : + resolution'd X n = d X (n + 1) := rfl + +/-- The shifted resolution of a topological representation by `1` degree. -/ +abbrev resolution' (X : TopRep k G) : CochainComplex (TopRep k G) ℕ := + CochainComplex.of (resolution'X X) + (resolution'd X) (fun n ↦ d_comp_d X (n + 1)) + +set_option allowUnsafeReducibility true in +attribute [local reducible] CategoryTheory.Functor.mapHomologicalComplex + +/-- The homogeneous cochains of a topological representation. -/ +abbrev homogeneousCochains (X : TopRep k G) : + CochainComplex (TopModuleCat k) ℕ := + ((invariantsFunctor k G).mapHomologicalComplex _).obj (resolution' X) + +lemma homogeneousCochains.d_eq (X : TopRep k G) (i : ℕ) : + (homogeneousCochains X).d i (i + 1) = + (invariantsFunctor k G).map (d X (i + 1)) := by + dsimp only + rw [← resolution'd_eq, CochainComplex.of_d] + +lemma homogeneousCochains.d_apply (X : TopRep k G) (i : ℕ) + (σ : (homogeneousCochains X).X i) : + ((homogeneousCochains X).d i (i + 1)).hom σ = (d X (i + 1)).hom σ := by + rw [homogeneousCochains.d_eq] + dsimp [ContIntertwiningMap.mapInvariants_apply] + +/-- The continuous cohomology of a continuous representation defined by taking homology +of the homogeneous cochains. -/ +noncomputable abbrev _root_.continuousCohomology (n : ℕ) (A : TopRep k G) : + TopModuleCat k := (homogeneousCochains A).homology n + +end TopRep + +namespace ContinuousCohomology + +open TopRep + +/-- 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 homogeneous cochains. -/ +noncomputable abbrev cocycles (A : TopRep k G) (n : ℕ) : + TopModuleCat k := (homogeneousCochains A).cycles n + +/-- The natural map from `n`-cocycles to `n`th continuous cohomology for a `k`-linear +`G`-representation `A`. -/ +noncomputable abbrev π (A : TopRep k G) (n : ℕ) := (homogeneousCochains A).homologyπ n + +end ContinuousCohomology diff --git a/Mathlib/RepresentationTheory/Homological/ContCohomology/Functoriality.lean b/Mathlib/RepresentationTheory/Homological/ContCohomology/Functoriality.lean new file mode 100644 index 00000000000000..8e7e4e8a453839 --- /dev/null +++ b/Mathlib/RepresentationTheory/Homological/ContCohomology/Functoriality.lean @@ -0,0 +1,165 @@ +/- +Copyright (c) 2026 Yunzhou Xie. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Edison Xie, Richard Hill +-/ +module + +public import Mathlib.RepresentationTheory.Homological.ContCohomology.Basic + +/-! +# Functoriality of continuous cohomology + +Given topological groups `G` and `H`, a continuous group homomorphism `φ : H →ₜ* G`, a topological +representation `X` of `G`, a topological representation `Y` of `H`, and a morphism of topological +`H`-representations `f : res φ X ⟶ Y`, we construct a cochain map +`homogeneousCochains X ⟶ homogeneousCochains Y` and hence maps on continuous cohomology +`Hⁿ(G, X) ⟶ Hⁿ(H, Y)`. + +## Main definitions + +* `ContinuousCohomology.cochainsMap φ f` : the cochain map + `homogeneousCochains X ⟶ homogeneousCochains Y` induced by `φ : H →ₜ* G` and + `f : res φ X ⟶ Y`, sending an invariant function `σ : C(G, C(G, ⋯))` to `f ∘ σ ∘ φ`. +* `ContinuousCohomology.map φ f n` : the induced map `Hⁿ(G, X) ⟶ Hⁿ(H, Y)` on continuous + cohomology. +-/ + +@[expose] public section + +universe u v + +open CategoryTheory + +namespace ContinuousCohomology + +open TopRep ContRepresentation + +variable {k : Type u} {G H K : Type v} [Ring k] [TopologicalSpace k] + [Group G] [TopologicalSpace G] [IsTopologicalGroup G] + [Group H] [TopologicalSpace H] [IsTopologicalGroup H] + [Group K] [TopologicalSpace K] [IsTopologicalGroup K] + {X : TopRep k G} {Y : TopRep k H} {Z : TopRep k K} + +set_option allowUnsafeReducibility true in +attribute [local reducible] CategoryTheory.Functor.mapHomologicalComplex + +/-- The morphisms between the levels of the standard resolutions of `X` and `Y` induced by a +continuous group homomorphism `φ : H →ₜ* G` and a morphism `f : res φ X ⟶ Y`, given by +`F ↦ f ∘ F ∘ φ`. -/ +def resolutionMap (φ : H →ₜ* G) (f : res φ X ⟶ Y) : + (i : ℕ) → res φ (resolutionX X i) ⟶ resolutionX Y i + | 0 => f + | i + 1 => ofHom (coind₁ResMap φ (resolutionMap φ f i).hom) + +@[simp] +lemma resolutionMap_zero (φ : H →ₜ* G) (f : res φ X ⟶ Y) : + resolutionMap φ f 0 = f := rfl + +lemma resolutionMap_succ (φ : H →ₜ* G) (f : res φ X ⟶ Y) (i : ℕ) : + resolutionMap φ f (i + 1) = ofHom (coind₁ResMap φ (resolutionMap φ f i).hom) := rfl + +@[simp] +lemma resolutionMap_id (X : TopRep k G) (i : ℕ) : + resolutionMap (ContinuousMonoidHom.id G) (𝟙 X) i = 𝟙 (resolutionX X i) := by + induction i with + | zero => rfl + | succ i ih => + rw [resolutionMap_succ, ih] + ext F x + rfl + +lemma resolutionMap_comp (φ : H →ₜ* G) (ψ : K →ₜ* H) (f : res φ X ⟶ Y) (g : res ψ Y ⟶ Z) + (i : ℕ) : + resolutionMap (φ.comp ψ) (X := X) ((resFunctor (ψ : K →* H)).map f ≫ g) i = + (resFunctor (ψ : K →* H)).map (resolutionMap φ f i) ≫ resolutionMap ψ g i := by + induction i with + | zero => rfl + | succ i ih => + rw [resolutionMap_succ, resolutionMap_succ, resolutionMap_succ, ih] + ext F x + rfl + +/-- The maps `resolutionMap φ f` commute with the differentials of the resolutions. -/ +lemma resolutionMap_comp_d (φ : H →ₜ* G) (f : res φ X ⟶ Y) (i : ℕ) : + resolutionMap φ f i ≫ d Y i = + (resFunctor (φ : H →* G)).map (d X i) ≫ resolutionMap φ f (i + 1) := by + induction i with + | zero => rfl + | succ i ih => + ext : 1 + replace ih := congr($(ih).hom) + simp only [TopRep.hom_comp, resolutionMap_succ, TopRep.hom_ofHom, hom_d_succ, + ContIntertwiningMap.restrict_sub, ContIntertwiningMap.sub_comp, + ContIntertwiningMap.comp_sub, coind₁Map_comp_coind₁ResMap, + coind₁ResMap_comp_coind₁Map_restrict] at ih ⊢ + rw [ih, ← coind₁ResMap_comp_coind₁ι_restrict] + +/-- The cochain map `homogeneousCochains X ⟶ homogeneousCochains Y` induced by a continuous +group homomorphism `φ : H →ₜ* G` and a morphism of topological `H`-representations +`f : res φ X ⟶ Y`, sending an invariant function `σ : C(G, C(G, ⋯))` to `f ∘ σ ∘ φ`. -/ +@[simps! -isSimp f f_hom] +def cochainsMap (φ : H →ₜ* G) (f : res φ X ⟶ Y) : + homogeneousCochains X ⟶ homogeneousCochains Y where + f i := invariantsResMap φ (resolutionMap φ f (i + 1)) + comm' i j (hij : _ = _) := by + subst hij + rw [homogeneousCochains.d_eq, homogeneousCochains.d_eq, ← invariantsResMap_comp, + resolutionMap_comp_d, invariantsResMap_map_comp] + +@[simp] +lemma cochainsMap_id (X : TopRep k G) : + cochainsMap (ContinuousMonoidHom.id G) (𝟙 X) = 𝟙 (homogeneousCochains X) := by + ext i : 1 + rw [cochainsMap_f, resolutionMap_id] + ext v + rfl + +@[reassoc] +lemma cochainsMap_comp (φ : H →ₜ* G) (ψ : K →ₜ* H) (f : res φ X ⟶ Y) (g : res ψ Y ⟶ Z) : + cochainsMap (φ.comp ψ) (X := X) ((resFunctor (ψ : K →* H)).map f ≫ g) = + cochainsMap φ f ≫ cochainsMap ψ g := by + ext i v x + exact congr($(resolutionMap_comp φ ψ f g (i + 1)).hom v.1 x) + +/-- The map `Zⁿ(G, X) ⟶ Zⁿ(H, Y)` on cocycles induced by a continuous group homomorphism +`φ : H →ₜ* G` and a morphism of topological `H`-representations `f : res φ X ⟶ Y`. -/ +noncomputable abbrev cocyclesMap (φ : H →ₜ* G) (f : res φ X ⟶ Y) (n : ℕ) : + cocycles X n ⟶ cocycles Y n := + HomologicalComplex.cyclesMap (cochainsMap φ f) n + +@[simp] +lemma cocyclesMap_id (X : TopRep k G) (n : ℕ) : + cocyclesMap (ContinuousMonoidHom.id G) (𝟙 X) n = 𝟙 _ := by + simp [cocyclesMap] + +@[reassoc] +lemma cocyclesMap_comp (φ : H →ₜ* G) (ψ : K →ₜ* H) (f : res φ X ⟶ Y) (g : res ψ Y ⟶ Z) + (n : ℕ) : + cocyclesMap (φ.comp ψ) (X := X) ((resFunctor (ψ : K →* H)).map f ≫ g) n = + cocyclesMap φ f n ≫ cocyclesMap ψ g n := by + simp [cocyclesMap, ← HomologicalComplex.cyclesMap_comp, ← cochainsMap_comp] + +/-- The map `Hⁿ(G, X) ⟶ Hⁿ(H, Y)` on continuous cohomology induced by a continuous group +homomorphism `φ : H →ₜ* G` and a morphism of topological `H`-representations +`f : res φ X ⟶ Y`. -/ +noncomputable abbrev map (φ : H →ₜ* G) (f : res φ X ⟶ Y) (n : ℕ) : + continuousCohomology n X ⟶ continuousCohomology n Y := + HomologicalComplex.homologyMap (cochainsMap φ f) n + +@[reassoc] +theorem π_map (φ : H →ₜ* G) (f : res φ X ⟶ Y) (n : ℕ) : + π X n ≫ map φ f n = cocyclesMap φ f n ≫ π Y n := by + simp [map, cocyclesMap] + +@[simp] +lemma map_id (X : TopRep k G) (n : ℕ) : + map (ContinuousMonoidHom.id G) (𝟙 X) n = 𝟙 _ := by + simp [map] + +@[reassoc] +lemma map_comp (φ : H →ₜ* G) (ψ : K →ₜ* H) (f : res φ X ⟶ Y) (g : res ψ Y ⟶ Z) (n : ℕ) : + map (φ.comp ψ) (X := X) ((resFunctor (ψ : K →* H)).map f ≫ g) n = map φ f n ≫ map ψ g n := by + simp [map, ← HomologicalComplex.homologyMap_comp, ← cochainsMap_comp] + +end ContinuousCohomology diff --git a/Mathlib/RepresentationTheory/Homological/ContCohomology/LowDegree.lean b/Mathlib/RepresentationTheory/Homological/ContCohomology/LowDegree.lean new file mode 100644 index 00000000000000..3b56514a64514b --- /dev/null +++ b/Mathlib/RepresentationTheory/Homological/ContCohomology/LowDegree.lean @@ -0,0 +1,89 @@ +/- +Copyright (c) 2026 Richard Hill. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Richard Hill, Andrew Yang, Edison Xie +-/ +module + +public import Mathlib.RepresentationTheory.Homological.ContCohomology.Basic + +/-! +## Low degree continuous cohomology + +In this file we show that the zeroth continuous cohomology is isomorphic to the +invariants of the representation. +-/ + +@[expose] public section + +namespace ContinuousCohomology + +open CategoryTheory Functor TopRep ContRepresentation + +variable {k G : Type*} [Ring k] [Group G] [TopologicalSpace k] + [TopologicalSpace G] [IsTopologicalGroup G] + +set_option allowUnsafeReducibility true in +attribute [local reducible] CategoryTheory.Functor.mapHomologicalComplex + +variable (X : TopRep k G) + +lemma cocycles₀IsoAux (σ : (homogeneousCochains X).X 0) + (hσ : σ ∈ ((homogeneousCochains X).d 0 1).hom.ker) : σ.1 1 ∈ X.ρ.invariants := by + simp only [Nat.reduceAdd, LinearMap.mem_ker, ContinuousLinearMap.coe_coe, + Subtype.ext_iff, homogeneousCochains.d_apply _] at hσ + simp only [mem_invariants] + intro g + rw [d_succ, hom_sub, hom_ofHom, ContIntertwiningMap.sub_apply, d_zero, + ZeroMemClass.coe_zero, sub_eq_zero] at hσ + replace hσ := DFunLike.ext_iff.1 (DFunLike.ext_iff.1 hσ 1) g⁻¹ + simp only [Nat.reduceAdd, coind₁ι_toFun, ContinuousMap.const_apply, ConcreteCategory.hom_ofHom, + coind₁Map_toFun, ContinuousMap.comp_apply, ContinuousMap.coe_mk] at hσ + simpa [hσ] using DFunLike.ext_iff.1 (σ.2 g) 1 + +lemma mem_const_resol₀ (x : X) (hx : x ∈ X.ρ.invariants) : + ContinuousMap.const G x ∈ ((resolution' X).X 0).ρ.invariants := + ContRepresentation.mem_invariants _|>.1 fun _ ↦ ContinuousMap.ext fun _ ↦ hx _ + +lemma cocycles₀IsoAux' (x : X) (h : ContinuousMap.const G x ∈ ((resolution' X).X 0).ρ.invariants) : + ⟨ContinuousMap.const G x, h⟩ ∈ ((homogeneousCochains X).d 0 1).hom.ker := by + rw [LinearMap.mem_ker, Subtype.ext_iff, ContinuousLinearMap.coe_coe, + homogeneousCochains.d_apply] + simp [d_succ, hom_sub, ContIntertwiningMap.sub_apply, d_zero] + +/-- The isomorphism between the zeroth cocycles and the kernel of the zeroth differential. -/ +noncomputable abbrev cocycles₀Iso : cocycles X 0 ≅ + TopModuleCat.of k ((homogeneousCochains X).d 0 1).hom.ker := + Limits.KernelFork.mapIsoOfIsLimit ((homogeneousCochains X).cyclesIsKernel 0 1 (by simp)) + (TopModuleCat.isLimitKer _) (Iso.refl _) + +/-- The isomorphism between the kernel of the zeroth differential and +the invariants of a representation. -/ +def d₀kerIso : ((homogeneousCochains X).d 0 1).hom.ker ≃L[k] X.ρ.invariants where + toFun := fun ⟨σ, hσ⟩ ↦ ⟨σ.val 1, cocycles₀IsoAux X σ hσ⟩ + map_add' _ _ := rfl + map_smul' _ _ := rfl + invFun := fun ⟨x, hx⟩ ↦ ⟨⟨ContinuousMap.const G x, mem_const_resol₀ X x hx⟩, + cocycles₀IsoAux' X x (mem_const_resol₀ X x hx)⟩ + left_inv := fun ⟨⟨(x : C(G, X)), hx'⟩, hx⟩ ↦ by + ext g + rw [LinearMap.mem_ker, Subtype.ext_iff, ContinuousLinearMap.coe_coe, + homogeneousCochains.d_apply] at hx + simp only [Nat.reduceAdd, d_succ, d_zero, ConcreteCategory.hom_ofHom, hom_sub, + ContIntertwiningMap.sub_apply, coind₁ι_toFun, coind₁Map_toFun, ZeroMemClass.coe_zero, + sub_eq_zero, ContinuousMap.const_apply] at hx ⊢ + simpa using DFunLike.ext_iff.1 (DFunLike.ext_iff.1 hx g) 1 + right_inv _ := rfl + continuous_toFun := continuous_induced_rng.2 <| (continuous_eval_const 1).comp <| + (continuous_subtype_val.comp continuous_subtype_val) + continuous_invFun := continuous_induced_rng.2 <| continuous_induced_rng.2 <| + ContinuousMap.continuous_const'.comp continuous_subtype_val + +/-- The isomorphism between the zeroth continuous cohomology group and +the invariants of a representation. -/ +noncomputable def zeroIso (A : TopRep k G) : + continuousCohomology 0 A ≅ TopModuleCat.of k A.ρ.invariants := + (homogeneousCochains A).isoHomologyπ₀.symm ≪≫ cocycles₀Iso A ≪≫ + TopModuleCat.ofIso (d₀kerIso A) + +end ContinuousCohomology diff --git a/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean b/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean index fb27295e645d1f..f7db8e10cb5783 100644 --- a/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean +++ b/Mathlib/RepresentationTheory/Homological/GroupCohomology/Functoriality.lean @@ -112,6 +112,11 @@ noncomputable abbrev cocyclesMap (n : ℕ) : groupCohomology.cocycles A n ⟶ groupCohomology.cocycles B n := HomologicalComplex.cyclesMap (cochainsMap f φ) n +lemma cochainsMap_congr {f g : G →* H} {φ : res f A ⟶ B} {ψ : res g A ⟶ B} (hfg : f = g) + (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) : + cochainsMap f φ = cochainsMap g ψ := by + subst hfg; congr; ext; simp [hφψ] + @[simp] lemma cocyclesMap_id : cocyclesMap (MonoidHom.id G) (𝟙 B) n = 𝟙 _ := HomologicalComplex.cyclesMap_id _ _ @@ -137,6 +142,11 @@ noncomputable abbrev map (n : ℕ) : groupCohomology A n ⟶ groupCohomology B n := HomologicalComplex.homologyMap (cochainsMap f φ) n +lemma map_congr {f g : G →* H} {φ : res f A ⟶ B} {ψ : res g A ⟶ B} (hfg : f = g) + (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) (n : ℕ) : + map f φ n = map g ψ n := by + subst hfg; congr; ext; simp [hφψ] + set_option backward.isDefEq.respectTransparency false in @[reassoc, elementwise] theorem π_map (n : ℕ) : @@ -161,6 +171,23 @@ theorem map_id_comp {A B C : Rep k G} (φ : A ⟶ B) (ψ : B ⟶ C) (n : ℕ) : map (MonoidHom.id G) φ n ≫ map (MonoidHom.id G) ψ n := by rw [map, cochainsMap_id_comp, HomologicalComplex.homologyMap_comp] +/-- The isomorphism between cohomology groups induced by a group isomorphism `e : G ≃* H` and a +isomorphism between representations (restricted by `e`). -/ +@[simps] +noncomputable def mapIso (e : G ≃* H) (e' : B.V ≃ₗ[k] A.V) + (he : ∀ g, e' ∘ₗ B.ρ g = A.ρ (e g) ∘ₗ e') (n : ℕ) : + groupCohomology B n ≅ groupCohomology A n where + hom := groupCohomology.map e.symm (ofHom ⟨e', fun h ↦ by simp [he]⟩) n + inv := groupCohomology.map e (ofHom ⟨e'.symm, fun g ↦ by + rw [e'.toLinearMap_symm_comp_eq, ← LinearMap.comp_assoc] + simp [he, LinearMap.comp_assoc]⟩) n + hom_inv_id := by + rw [← groupCohomology.map_comp, ← groupCohomology.map_id] + exact map_congr (by simp) (by simp [res_id]) n + inv_hom_id := by + rw [← groupCohomology.map_comp, ← groupCohomology.map_id] + exact groupCohomology.map_congr (by simp) e'.comp_symm n + /-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : Res(f)(A) ⟶ B`, this is the induced map sending `x : H → A` to `(g : G) ↦ φ (x (f g))`. -/ noncomputable abbrev cochainsMap₁ : @@ -524,7 +551,7 @@ noncomputable def resNatTrans (n : ℕ) : simp only [functor_map, Functor.comp_map, ← cancel_epi (groupCohomology.π _ n), HomologicalComplex.homologyπ_naturality_assoc, HomologicalComplex.homologyπ_naturality, ← HomologicalComplex.cyclesMap_comp_assoc, - ← cochainsMap_comp, res_obj_ρ, Category.comp_id, Rep.hom_id] + ← cochainsMap_comp, res_obj_ρ, Category.comp_id] rfl set_option backward.isDefEq.respectTransparency false in diff --git a/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean b/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean index 90993978ab1507..7367d9ff4f49ff 100644 --- a/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean +++ b/Mathlib/RepresentationTheory/Homological/GroupHomology/Functoriality.lean @@ -64,6 +64,11 @@ noncomputable def chainsMap : simp [Fin.comp_contractNth, map_add, inhomogeneousChains.d, Rep.hom_comm_apply φ] rfl +lemma chainsMap_congr {f g : G →* H} {φ : A ⟶ res f B} {ψ : A ⟶ res g B} (hfg : f = g) + (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) : + chainsMap f φ = chainsMap g ψ := by + subst hfg; congr; ext; simp [hφψ] + @[reassoc (attr := simp)] lemma lsingle_comp_chainsMap_f (n : ℕ) (x : Fin n → G) : ModuleCat.ofHom (lsingle x) ≫ (chainsMap f φ).f n = @@ -153,6 +158,11 @@ noncomputable abbrev map (n : ℕ) : groupHomology A n ⟶ groupHomology B n := HomologicalComplex.homologyMap (chainsMap f φ) n +lemma map_congr {f g : G →* H} {φ : A ⟶ res f B} {ψ : A ⟶ res g B} (hfg : f = g) + (hφψ : φ.hom.toLinearMap = ψ.hom.toLinearMap) (n : ℕ) : + map f φ n = map g ψ n := by + subst hfg; congr; ext; simp [hφψ] + set_option backward.isDefEq.respectTransparency false in @[reassoc, elementwise] theorem π_map (n : ℕ) : @@ -177,6 +187,23 @@ theorem map_id_comp {A B C : Rep k G} (φ : A ⟶ B) (ψ : B ⟶ C) (n : ℕ) : map (MonoidHom.id G) φ n ≫ map (MonoidHom.id G) ψ n := by rw [map, chainsMap_id_comp, HomologicalComplex.homologyMap_comp] +/-- The isomorphism between homology groups induced by a group isomorphism `e : G ≃* H` and a +isomorphism between representations (restricted by `e`). -/ +@[simps] +noncomputable def mapIso (e : G ≃* H) (e' : A.V ≃ₗ[k] B.V) + (he : ∀ g, e' ∘ₗ A.ρ g = B.ρ (e g) ∘ₗ e') (n : ℕ) : + groupHomology A n ≅ groupHomology B n where + hom := groupHomology.map (A := A) e (ofHom ⟨e', by simp [he]⟩) n + inv := groupHomology.map (A := B) e.symm (ofHom ⟨e'.symm, fun h ↦ by + rw [LinearEquiv.toLinearMap_symm_comp_eq, ← LinearMap.comp_assoc] + simp [he, LinearMap.comp_assoc]⟩) n + hom_inv_id := by + rw [← groupHomology.map_comp, ← groupHomology.map_id] + exact groupHomology.map_congr e.coe_monoidHom_symm_comp_coe_monoidHom e'.symm_comp n + inv_hom_id := by + rw [← groupHomology.map_comp, ← groupHomology.map_id] + exact groupHomology.map_congr e.coe_monoidHom_comp_coe_monoidHom_symm e'.comp_symm n + /-- Given a group homomorphism `f : G →* H` and a representation morphism `φ : A ⟶ Res(f)(B)`, this is the induced map sending `∑ aᵢ·gᵢ : G →₀ A` to `∑ φ(aᵢ)·f(gᵢ) : H →₀ B`. -/ noncomputable abbrev chainsMap₁ : ModuleCat.of k (G →₀ A) ⟶ ModuleCat.of k (H →₀ B) := @@ -574,7 +601,7 @@ and `Y - ∑ aᵢ·sᵢ` is a cycle. -/ rcases chains₁ToCoinvariantsKer_surjective (res S.subtype A) ⟨d₁₀ A Y, this⟩ with ⟨(Z : S →₀ A), hZ⟩ have H : d₁₀ A (Y - mapDomain S.subtype Z) = 0 := by - simpa [map_sub, sub_eq_zero, chains₁ToCoinvariantsKer, - LinearMap.sub_apply, d₁₀, + simpa [map_sub, sub_eq_zero, chains₁ToCoinvariantsKer, -LinearMap.sub_apply, d₁₀, sum_mapDomain_index_inj] using! Subtype.ext_iff.1 hZ.symm use H1π A ⟨Y - mapDomain S.subtype Z, H⟩ simp only [H1CoresCoinf_X₃, H1CoresCoinf_X₂, H1CoresCoinf_g, @@ -826,11 +853,9 @@ noncomputable def coresNatTrans (n : ℕ) : simp only [← cancel_epi (groupHomology.π _ n), Functor.comp_map, functor_map, HomologicalComplex.homologyπ_naturality_assoc, HomologicalComplex.homologyπ_naturality, ← HomologicalComplex.cyclesMap_comp_assoc, - ← chainsMap_comp, res_obj_ρ, Rep.hom_id, Category.id_comp] + ← chainsMap_comp, Category.id_comp] rfl - - set_option backward.isDefEq.respectTransparency false in /-- Given a normal subgroup `S ≤ G`, this sends `A : Rep k G` to the `n`th "coinflation" map `Hₙ(G, A) ⟶ Hₙ(G ⧸ S, A_S)` induced by the quotient maps `G →* G ⧸ S` and `A →ₗ A_S`. -/ diff --git a/Mathlib/RepresentationTheory/Homological/Resolution.lean b/Mathlib/RepresentationTheory/Homological/Resolution.lean index e21a60eb83e1c0..ec47f3ab77cab3 100644 --- a/Mathlib/RepresentationTheory/Homological/Resolution.lean +++ b/Mathlib/RepresentationTheory/Homological/Resolution.lean @@ -54,10 +54,10 @@ computing group (co)homology. ## Main definitions - * `groupCohomology.resolution.ofMulActionBasis` - * `classifyingSpaceUniversalCover` - * `Rep.standardComplex.forget₂ToModuleCatHomotopyEquiv` - * `Rep.standardResolution` +* `groupCohomology.resolution.ofMulActionBasis` +* `classifyingSpaceUniversalCover` +* `Rep.standardComplex.forget₂ToModuleCatHomotopyEquiv` +* `Rep.standardResolution` TODO: There's bad DefEq abuses in `Action` and the way we do `Rep.standardComplex` should be unified with continuous cohomology, therefore we should remove the use of `Action` in `Rep` which diff --git a/Mathlib/RepresentationTheory/Induced.lean b/Mathlib/RepresentationTheory/Induced.lean index 9033419a235a77..c7bfc7fafff3a5 100644 --- a/Mathlib/RepresentationTheory/Induced.lean +++ b/Mathlib/RepresentationTheory/Induced.lean @@ -110,7 +110,7 @@ noncomputable def indMap {A B : Rep k G} (f : A ⟶ B) : ind φ A ⟶ ind φ B : variable (k) in /-- Given a group homomorphism `φ : G →* H`, this is the functor sending a `G`-representation `A` to the induced `H`-representation `ind φ A`, with action on maps induced by left tensoring. -/ -@[simps obj map] +@[implicit_reducible, simps obj map] noncomputable def indFunctor : Rep.{w} k G ⥤ Rep k H where obj A := ind φ A map f := indMap φ f @@ -152,8 +152,6 @@ noncomputable def indResHomEquiv (A : Rep.{max w v' u} k G) (B : Rep.{max w v' u simpa using (hom_comm_apply f h⁻¹ (IndV.mk φ A.ρ 1 a)).symm right_inv _ := by ext; simp -set_option backward.defeqAttrib.useBackward true in -set_option backward.isDefEq.respectTransparency false in variable (k) in /-- Given a group homomorphism `φ : G →* H`, the induction functor `Rep k G ⥤ Rep k H` is left adjoint to the restriction functor along `φ`. -/ @@ -262,8 +260,7 @@ noncomputable def coinvariantsTensorIndNatIso : (coinvariantsTensor k H).obj (ind φ A) ≅ resFunctor φ ⋙ (coinvariantsTensor k G).obj A := NatIso.ofComponents (fun B => coinvariantsTensorIndIso φ A B) fun {X Y} f => by ext - simp [coinvariantsTensorIndHom, coinvariantsTensorMk, - hom_comm_apply, Representation.IntertwiningMap.toLinearMap_apply] + simp [coinvariantsTensorIndHom, coinvariantsTensorMk, hom_comm_apply] end end Rep diff --git a/Mathlib/RepresentationTheory/Invariants.lean b/Mathlib/RepresentationTheory/Invariants.lean index f6394781abb975..e78c870f3f9f46 100644 --- a/Mathlib/RepresentationTheory/Invariants.lean +++ b/Mathlib/RepresentationTheory/Invariants.lean @@ -246,7 +246,7 @@ abbrev quotientToInvariants : Rep k (G ⧸ S) := Rep.of (A.ρ.quotientToInvarian variable (k G) /-- The functor sending a representation to its submodule of invariants. -/ -@[simps! obj_carrier map_hom] +@[implicit_reducible, simps! obj_carrier map_hom] noncomputable def invariantsFunctor : Rep.{w} k G ⥤ ModuleCat k where obj A := ModuleCat.of k A.ρ.invariants map {A B} f := ModuleCat.ofHom <| (f.hom ∘ₗ A.ρ.invariants.subtype).codRestrict @@ -258,7 +258,6 @@ instance : (invariantsFunctor k G).PreservesZeroMorphisms where instance : (invariantsFunctor k G).Additive where instance : (invariantsFunctor k G).Linear k where -set_option backward.isDefEq.respectTransparency false in variable {G} in /-- Given a normal subgroup S ≤ G, this is the functor sending a `G`-representation `A` to the `G ⧸ S`-representation it induces on `A^S`. -/ @@ -266,7 +265,7 @@ noncomputable def quotientToInvariantsFunctor (S : Subgroup G) [S.Normal] : Rep.{w} k G ⥤ Rep k (G ⧸ S) where obj X := X.quotientToInvariants S map {X Y} f := Rep.ofHom ⟨((invariantsFunctor k S).map ((Rep.resFunctor S.subtype).map f)).hom, - fun g ↦ QuotientGroup.induction_on g fun g ↦ by ext x; simp [hom_comm_apply]⟩ + fun g ↦ QuotientGroup.induction_on g fun g ↦ by ext; simp [hom_comm_apply]⟩ set_option backward.isDefEq.respectTransparency false in /-- The adjunction between the functor equipping a module with the trivial representation, and diff --git a/Mathlib/RepresentationTheory/Rep/Basic.lean b/Mathlib/RepresentationTheory/Rep/Basic.lean index fdbabd2bf828ba..4733606c6e9568 100644 --- a/Mathlib/RepresentationTheory/Rep/Basic.lean +++ b/Mathlib/RepresentationTheory/Rep/Basic.lean @@ -436,7 +436,7 @@ end setup variable (k G) in /-- The functor equipping a module with the trivial representation. -/ -@[simps! obj_V map_hom] +@[implicit_reducible, simps! obj_V map_hom] def trivialFunctor : ModuleCat.{w} k ⥤ Rep.{w} k G where obj V := trivial k G V map f := ofHom ⟨f.hom, fun _ ↦ rfl⟩ diff --git a/Mathlib/RepresentationTheory/Rep/Res.lean b/Mathlib/RepresentationTheory/Rep/Res.lean index 55467f78b051bd..284c9079d71112 100644 --- a/Mathlib/RepresentationTheory/Rep/Res.lean +++ b/Mathlib/RepresentationTheory/Rep/Res.lean @@ -9,6 +9,7 @@ public import Mathlib.RepresentationTheory.Rep.Basic /-! # Restriction of representations + Given a group homomorphism `f : H →* G`, we have the restriction functor `resFunctor f : Rep k G ⥤ Rep k H` which sends a `G`-representation `ρ` to the `H`-representation `ρ.comp f`. @@ -25,32 +26,48 @@ open CategoryTheory namespace Rep +/-- The map induced by a monoid homomorphism `f : H →* G` on morphisms between +`G`-representations. -/ +@[expose, implicit_reducible] +def resMap {X Y : Rep k G} (f : H →* G) (p : X ⟶ Y) : + of (X := X.V) (X.ρ.comp f) ⟶ of (X := Y.V) (Y.ρ.comp f) := + ofHom ⟨p.hom, fun h ↦ by simpa using p.hom.2 (f h)⟩ + /-- The restriction functor `Rep R G ⥤ Rep R H` for a subgroup `H` of `G`. -/ abbrev resFunctor (f : H →* G) : Rep.{t} k G ⥤ Rep k H where obj A := of (X := A.V) (A.ρ.comp f) - map f' := ofHom ⟨f'.hom, fun h ↦ by simpa using f'.hom.2 (f h)⟩ + map f' := resMap f f' /-- The restriction of `X : Rep k G` associated to a monoid homomorphism `f : H →* G` -/ abbrev res (f : H →* G) (M : Rep k G) := (resFunctor f).obj M variable (f : H →* G) (M : Rep k G) +lemma res_id : res (MonoidHom.id G) M = M := rfl + @[simp] lemma res_obj_ρ : (res f M).ρ = (M.ρ.comp f) := rfl lemma coe_res_obj_ρ' (h : H) : (res f M).ρ h = M.ρ (f h) := rfl lemma res_obj_V : (res f M).V = M.V := rfl -lemma res_map_hom_toLinearMap {M N : Rep k G} (p : M ⟶ N) : - ((resFunctor f).map p).hom.toLinearMap = p.hom.toLinearMap := rfl +@[simp] +lemma resMap_hom_toLinearMap {M N : Rep k G} (p : M ⟶ N) : + (resMap f p).hom.toLinearMap = p.hom.toLinearMap := rfl + +@[deprecated (since := "26/06/2026")] +alias res_map_hom_toLinearMap := resMap_hom_toLinearMap + +@[simp] +lemma resMap_hom_apply {M N : Rep k G} (p : M ⟶ N) (x : M.V) : + @DFunLike.coe (Representation.IntertwiningMap (M.ρ.comp f) (N.ρ.comp f)) _ _ _ + (resMap f p).hom x = p.hom x := rfl section instance : (resFunctor (k := k) f).Faithful where map_injective h := by - ext : 2 - rw [Rep.hom_ext_iff, Representation.IntertwiningMap.ext_iff] at h - simpa using h + simpa [Rep.hom_ext_iff, Representation.IntertwiningMap.ext_iff] using h /-- Morphism between `X Y : Rep k G` can be lifted from restrictions associated with `f : H →* G` when `f` is surjective. -/ @@ -63,21 +80,13 @@ lemma liftHomOfSurj_toLinearMap {X Y : Rep k G} (hf : Function.Surjective f) f'.hom.toLinearMap := rfl lemma full_res (hf : (⇑f).Surjective) : (resFunctor (k := k) f).Full where - map_surjective {X Y} f' := ⟨liftHomOfSurj f hf f', by - ext : 2; rw [res_map_hom_toLinearMap, liftHomOfSurj_toLinearMap]⟩ + map_surjective {X Y} f' := ⟨liftHomOfSurj f hf f', by ext; simp⟩ instance : (resFunctor (k := k) f).Additive where - map_add {_ _} _ _ := by - ext : 2; - simp only [add_hom, Representation.IntertwiningMap.add_toLinearMap] - rfl + map_add {_ _} _ _ := by ext : 2; simp [add_hom] instance {k : Type u} [CommSemiring k] : (resFunctor (k := k) f).Linear k where - map_smul {X Y} l r := by - ext : 2; - rw [smul_hom, Representation.IntertwiningMap.toLinearMap_smul, - res_map_hom_toLinearMap, smul_hom, Representation.IntertwiningMap.toLinearMap_smul, - res_map_hom_toLinearMap] + map_smul {_ _} _ _ := by ext : 2; simp [smul_hom] noncomputable section diff --git a/Mathlib/RingTheory/AdicCompletion/RingHom.lean b/Mathlib/RingTheory/AdicCompletion/RingHom.lean index 29a6258f804c40..a2fe215427b9c7 100644 --- a/Mathlib/RingTheory/AdicCompletion/RingHom.lean +++ b/Mathlib/RingTheory/AdicCompletion/RingHom.lean @@ -9,6 +9,7 @@ public import Mathlib.RingTheory.AdicCompletion.Algebra /-! # Lift of ring homomorphisms to adic completions + Let `R`, `S` be rings, `I` be an ideal of `S`. In this file we prove that a compatible family of ring homomorphisms from a ring `R` to `S ⧸ I ^ n` can be lifted to a ring homomorphism `R →+* AdicCompletion I S`. diff --git a/Mathlib/RingTheory/AdjoinRoot.lean b/Mathlib/RingTheory/AdjoinRoot.lean index 46e60c23c827e6..bf58bd9bd4f793 100644 --- a/Mathlib/RingTheory/AdjoinRoot.lean +++ b/Mathlib/RingTheory/AdjoinRoot.lean @@ -977,6 +977,42 @@ theorem quotEquivQuotMap_symm_apply_mk (f g : R[X]) (I : Ideal R) : end +section TensorProduct +variable {R S T U : Type*} [CommRing R] [CommRing S] [CommRing T] [Algebra R S] [Algebra R T] + [CommRing U] [Algebra R U] {p : Polynomial S} + +open Algebra TensorProduct + +variable (p) in +/-- Adjoining a root is preserved under base change. -/ +def tensorAlgEquiv (p : S[X]) (q : (T ⊗[R] S)[X]) (h : p.map includeRight.toRingHom = q) : + T ⊗[R] AdjoinRoot p ≃ₐ[T] AdjoinRoot q := by + refine .ofAlgHom + (Algebra.TensorProduct.lift (algHom T T _) + (mapAlgHom includeRight p q <| by exact h.symm.dvd) fun _ _ ↦ .all ..) + (liftAlgHom _ (Algebra.TensorProduct.map (AlgHom.id T T) + (((Algebra.ofId S (AdjoinRoot p))).restrictScalars R)) (1 ⊗ₜ root _) ?_) ?_ ?_ + · simp only [← h, AlgHom.toRingHom_eq_coe] + rw [Polynomial.eval₂_map] + change Polynomial.eval₂ ((Algebra.TensorProduct.map (AlgHom.id R T) _).comp _).toRingHom _ _ = _ + simp only [map_comp_includeRight, AlgHom.toRingHom_eq_coe, AlgHom.comp_toRingHom, + AlgHom.coe_restrictScalars, ← Polynomial.eval₂_map] + change Polynomial.eval₂ _ ((RingHomClass.toRingHom includeRight) (root p)) (p.map (of _)) = _ + rw [Polynomial.eval₂_hom] + simp [Polynomial.eval_map] + · ext + · simp [Algebra.ofId_apply] + simp + · ext : 3 <;> simp + +@[simp] lemma tensorAlgEquiv_root (p : S[X]) (q : Polynomial (T ⊗[R] S)) (h) : + tensorAlgEquiv p q h (1 ⊗ₜ root p) = root q := by simp [tensorAlgEquiv] + +@[simp] lemma tensorAlgEquiv_of (p : S[X]) (q : Polynomial (T ⊗[R] S)) (h) {x : S} : + tensorAlgEquiv p q h (1 ⊗ₜ of p x) = of q (1 ⊗ₜ x):= by simp [tensorAlgEquiv] + +end TensorProduct + end AdjoinRoot namespace PowerBasis @@ -1014,7 +1050,7 @@ theorem quotientEquivQuotientMinpolyMap_apply_mk (pb : PowerBasis R S) (I : Idea (Ideal.span ({(minpoly R pb.gen).map (Ideal.Quotient.mk I)} : Set (Polynomial (R ⧸ I)))) (g.map (Ideal.Quotient.mk I)) := by rw [PowerBasis.quotientEquivQuotientMinpolyMap, AlgEquiv.trans_apply, AlgEquiv.ofRingEquiv_apply, - quotientEquiv_mk, AlgEquiv.coe_ringEquiv', AdjoinRoot.equiv'_symm_apply, PowerBasis.lift_aeval, + quotientEquiv_mk, AlgEquiv.coe_ringEquiv, AdjoinRoot.equiv'_symm_apply, PowerBasis.lift_aeval, AdjoinRoot.aeval_eq, AdjoinRoot.quotEquivQuotMap_apply_mk] -- This lemma should have the simp tag but this causes a lint issue. diff --git a/Mathlib/RingTheory/Algebraic/Integral.lean b/Mathlib/RingTheory/Algebraic/Integral.lean index 7cf576e5fdc5b7..7d74915b28c242 100644 --- a/Mathlib/RingTheory/Algebraic/Integral.lean +++ b/Mathlib/RingTheory/Algebraic/Integral.lean @@ -129,9 +129,6 @@ theorem transcendental_aeval_iff {r : A} {f : K[X]} : variable [Field L] [Algebra K L] -theorem AlgHom.bijective [FiniteDimensional K L] (ϕ : L →ₐ[K] L) : Function.Bijective ϕ := - (Algebra.IsAlgebraic.of_finite K L).algHom_bijective ϕ - variable (K L) in /-- Bijection between algebra equivalences and algebra homomorphisms -/ noncomputable abbrev algEquivEquivAlgHom [FiniteDimensional K L] : diff --git a/Mathlib/RingTheory/Algebraic/MvPolynomial.lean b/Mathlib/RingTheory/Algebraic/MvPolynomial.lean index 632a314528adee..40baac310e5252 100644 --- a/Mathlib/RingTheory/Algebraic/MvPolynomial.lean +++ b/Mathlib/RingTheory/Algebraic/MvPolynomial.lean @@ -44,7 +44,7 @@ theorem transcendental_supported_polynomial_aeval_X {i : σ} {s : Set σ} (h : i (Polynomial.mapAlgEquiv (supportedEquivMvPolynomial s).symm).toAlgHom replace hf : Function.Injective u := by simp only [AlgHom.coe_comp, Subalgebra.coe_val, - AlgEquiv.coe_algHom, AlgEquiv.coe_trans, Function.comp_assoc, u] + AlgEquiv.coe_toAlgHom, AlgEquiv.coe_trans, Function.comp_assoc, u] apply Subtype.val_injective.comp simp only [EquivLike.comp_injective] apply hf.comp @@ -59,7 +59,7 @@ theorem transcendental_supported_polynomial_aeval_X {i : σ} {s : Set σ} (h : i · ext1 simp [Set.subtypeInsertEquivOption, Subalgebra.algebraMap_eq, optionEquivLeft_symm_apply] · simp [Set.subtypeInsertEquivOption, h1, optionEquivLeft_symm_apply] - simpa only [h2, v, AlgHom.coe_comp, AlgEquiv.coe_algHom, + simpa only [h2, v, AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, EquivLike.injective_comp, AlgHom.coe_restrictScalars'] using hf theorem transcendental_polynomial_aeval_X (i : σ) {f : R[X]} (hf : Transcendental R f) : diff --git a/Mathlib/RingTheory/Bialgebra/Equiv.lean b/Mathlib/RingTheory/Bialgebra/Equiv.lean index 819381736fc847..877a4943aae75e 100644 --- a/Mathlib/RingTheory/Bialgebra/Equiv.lean +++ b/Mathlib/RingTheory/Bialgebra/Equiv.lean @@ -280,10 +280,10 @@ lemma apply_symm_apply (e : A ≃ₐc[R] B) : ∀ x, e (e.symm x) = x := e.toEqu lemma symm_apply_apply (e : A ≃ₐc[R] B) : ∀ x, e.symm (e x) = x := e.toEquiv.symm_apply_apply @[simp] lemma comp_symm (e : A ≃ₐc[R] B) : (e : A →ₐc[R] B).comp e.symm = .id R B := - BialgHom.coe_algHom_injective e.toAlgEquiv.comp_symm + BialgHom.coe_toAlgHom_injective e.toAlgEquiv.comp_symm @[simp] lemma symm_comp (e : A ≃ₐc[R] B) : (e.symm : B →ₐc[R] A).comp e = .id R A := - BialgHom.coe_algHom_injective e.toAlgEquiv.symm_comp + BialgHom.coe_toAlgHom_injective e.toAlgEquiv.symm_comp @[simp] lemma toRingEquiv_toRingHom (e : A ≃ₐc[R] B) : ((e : A ≃+* B) : A →+* B) = e := rfl @[simp] lemma toAlgEquiv_toRingHom (e : A ≃ₐc[R] B) : ((e : A ≃ₐ[R] B) : A →+* B) = e := rfl diff --git a/Mathlib/RingTheory/Bialgebra/Hom.lean b/Mathlib/RingTheory/Bialgebra/Hom.lean index 7598da37a0d9ea..2e23be000a224b 100644 --- a/Mathlib/RingTheory/Bialgebra/Hom.lean +++ b/Mathlib/RingTheory/Bialgebra/Hom.lean @@ -194,10 +194,12 @@ theorem coe_coalgHom_injective : Function.Injective ((↑) : (A →ₐc[R] B) fun φ₁ φ₂ H => coe_fn_injective <| show ((φ₁ : A →ₗc[R] B) : A → B) = ((φ₂ : A →ₗc[R] B) : A → B) from congr_arg _ H -theorem coe_algHom_injective : Function.Injective ((↑) : (A →ₐc[R] B) → A →ₐ[R] B) := +theorem coe_toAlgHom_injective : Function.Injective ((↑) : (A →ₐc[R] B) → A →ₐ[R] B) := fun φ₁ φ₂ H => coe_fn_injective <| show ((φ₁ : A →ₐ[R] B) : A → B) = ((φ₂ : A →ₐ[R] B) : A → B) from congr_arg _ H +@[deprecated (since := "2026-05-05")] alias coe_algHom_injective := coe_toAlgHom_injective + theorem coe_linearMap_injective : Function.Injective ((↑) : (A →ₐc[R] B) → A →ₗ[R] B) := CoalgHom.coe_linearMap_injective.comp coe_coalgHom_injective diff --git a/Mathlib/RingTheory/Binomial.lean b/Mathlib/RingTheory/Binomial.lean index 48cf0d55e89325..ede4089fd6784e 100644 --- a/Mathlib/RingTheory/Binomial.lean +++ b/Mathlib/RingTheory/Binomial.lean @@ -523,7 +523,7 @@ theorem add_choose_eq [Ring R] [BinomialRing R] {r s : R} (k : ℕ) (h : Commute ← descPochhammer_eq_factorial_smul_choose, smul_sum, descPochhammer_smeval_add _ h] refine sum_congr rfl ?_ intro x hx - rw [← Nat.choose_mul_factorial_mul_factorial (antidiagonal.fst_le hx), + rw [← Nat.choose_mul_factorial_mul_factorial (HasAntidiagonal.antidiagonal.fst_le hx), tsub_eq_of_eq_add_rev (List.Nat.mem_antidiagonal.mp hx).symm, mul_assoc, nsmul_eq_mul, Nat.cast_mul, Nat.cast_mul, ← mul_assoc _ (x.1.factorial : R), mul_assoc _ (x.2.factorial : R), ← mul_assoc (x.2.factorial : R), Nat.cast_commute x.2.factorial, diff --git a/Mathlib/RingTheory/Conductor.lean b/Mathlib/RingTheory/Conductor.lean index 69273c05b667de..e858142535f2eb 100644 --- a/Mathlib/RingTheory/Conductor.lean +++ b/Mathlib/RingTheory/Conductor.lean @@ -12,6 +12,7 @@ public import Mathlib.RingTheory.PowerBasis /-! # The conductor ideal + This file defines the conductor ideal of an element `x` of `R`-algebra `S`. This is the ideal of `S` consisting of all elements `a` such that for all `b` in `S`, the product `a * b` lies in the `R`-subalgebra of `S` generated by `x`. diff --git a/Mathlib/RingTheory/Congruence/Basic.lean b/Mathlib/RingTheory/Congruence/Basic.lean index 40ad52ee203bd3..cbd85e276d1fab 100644 --- a/Mathlib/RingTheory/Congruence/Basic.lean +++ b/Mathlib/RingTheory/Congruence/Basic.lean @@ -26,13 +26,12 @@ Most of the time you likely want to use the `Ideal.Quotient` API that is built o ## TODO -* Use this for `RingQuot` too. * Copy across more API from `Con` and `AddCon` in `Mathlib/GroupTheory/Congruence/`. -/ @[expose] public section -variable {α β R : Type*} +variable {α β R R' : Type*} namespace RingCon @@ -132,7 +131,7 @@ The API in this section is copied from `Mathlib/GroupTheory/Congruence/Defs.lean section Lattice -variable [Add R] [Mul R] {c d : RingCon R} +variable [Add R] [Mul R] [Add R'] [Mul R'] {c d : RingCon R} /-- For congruence relations `c, d` on a type `M` with multiplication and addition, `c ≤ d` iff `∀ x y ∈ M`, `x` is related to `y` by `d` if `x` is related to `y` by `c`. -/ @@ -143,7 +142,7 @@ instance : LE (RingCon R) where theorem le_def : c ≤ d ↔ ∀ {x y}, c x y → d x y := .rfl @[gcongr] -theorem comap_mono {R' : Type*} [Add R'] [Mul R'] +theorem comap_mono {F : Type*} [FunLike F R R'] [AddHomClass F R R'] [MulHomClass F R R'] {J J' : RingCon R'} {f : F} (h : J ≤ J') : J.comap f ≤ J'.comap f := @@ -259,7 +258,6 @@ theorem ringConGen_eq (r : R → R → Prop) : (fun _ _ h1 h2 c hc => c.mul (h1 c hc) <| h2 c hc)) (sInf_le le_ringConGen) - /-- The smallest congruence relation containing a binary relation `r` is contained in any congruence relation containing `r`. -/ theorem ringConGen_le {r : R → R → Prop} {c : RingCon R} : ringConGen r ≤ c ↔ r ≤ ⇑c := @@ -326,6 +324,36 @@ theorem sSup_eq_ringConGen (S : Set (RingCon R)) : congr! with x y simp +open scoped Function + +theorem le_comap_ringConGen {F} [FunLike F R' R] [MulHomClass F R' R] [AddHomClass F R' R] + (r : R → R → Prop) (f : F) : + ringConGen (r on f) ≤ (ringConGen r).comap f := + ringConGen_le.2 fun _ _ h => RingConGen.Rel.of _ _ h + +theorem comap_injective {F} [FunLike F R' R] [MulHomClass F R' R] [AddHomClass F R' R] + (f : F) (hf : Function.Surjective f) : + Function.Injective (comap · f) := + .of_comp (f := toCon) <| (Con.comap_injective f hf <| map_mul f).comp toCon_injective + +theorem comap_ringConGen_ringEquiv {R R'} [NonAssocSemiring R] [NonAssocSemiring R'] + (r : R' → R' → Prop) (f : R ≃+* R') : + (ringConGen r).comap f = ringConGen (r on f) := by + refine le_antisymm ?_ (le_comap_ringConGen _ _) + trans (ringConGen (r on ⇑f) |>.comap f.symm.toNonUnitalRingHom).comap f.toNonUnitalRingHom + · apply comap_mono + grw [← le_comap_ringConGen] + gcongr + simp [Function.onFun, RingEquiv.coe_toNonUnitalRingHom'] + · rw [← comap_nonUnitalRingHomComp] + simp + +-- This one probably needs the RingCon version of `Setoid.comap_surjective` +proof_wanted comap_ringConGen_equiv + {F} [FunLike F R' R] [MulHomClass F R' R] [AddHomClass F R' R] [EquivLike F R' R] + (r : R → R → Prop) (f : F) : + (ringConGen r).comap f = ringConGen (r on f) + end Lattice end RingCon diff --git a/Mathlib/RingTheory/Congruence/Defs.lean b/Mathlib/RingTheory/Congruence/Defs.lean index 16f78f06af0245..a8750b1c846e1b 100644 --- a/Mathlib/RingTheory/Congruence/Defs.lean +++ b/Mathlib/RingTheory/Congruence/Defs.lean @@ -26,7 +26,6 @@ Most of the time you likely want to use the `Ideal.Quotient` API that is built o ## TODO -* Use this for `RingQuot` too. * Copy across more API from `Con` and `AddCon` in `Mathlib/GroupTheory/Congruence/`. -/ @@ -159,6 +158,26 @@ def comap (J : RingCon R') (f : F) : theorem comap_rel {J : RingCon R'} {f : F} {x y : R} : J.comap f x y ↔ J (f x) (f y) := Iff.rfl +@[simp] +theorem comap_nonUnitalRingHomId {R} [NonUnitalNonAssocSemiring R] (J : RingCon R) : + J.comap (NonUnitalRingHom.id _) = J := rfl + +@[simp] +theorem comap_nonUnitalRingHomComp {R R' R''} + [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring R'] [NonUnitalNonAssocSemiring R''] + (J : RingCon R) (g : R' →ₙ+* R) (f : R'' →ₙ+* R') : + J.comap (g.comp f) = (J.comap g).comap f := rfl + +@[simp] +theorem comap_ringHomId {R} [NonAssocSemiring R] (J : RingCon R) : + J.comap (RingHom.id _) = J := rfl + +@[simp] +theorem comap_ringHomComp {R R' R''} + [NonAssocSemiring R] [NonAssocSemiring R'] [NonAssocSemiring R''] + (J : RingCon R) (g : R' →+* R) (f : R'' →+* R') : + J.comap (g.comp f) = (J.comap g).comap f := rfl + end Basic section Quotient @@ -390,8 +409,10 @@ instance [Add R] [CommMagma R] (c : RingCon R) : CommMagma c.Quotient := instance [Add R] [CommSemigroup R] (c : RingCon R) : CommSemigroup c.Quotient := inferInstanceAs <| CommSemigroup c.toCon.Quotient -instance [Add R] [Monoid R] (c : RingCon R) : Monoid c.Quotient := - inferInstanceAs <| Monoid c.toCon.Quotient +instance [Add R] [Monoid R] (c : RingCon R) : Monoid c.Quotient := fast_instance% + { __ : Monoid c.toCon.Quotient := inferInstanceAs _ + -- see https://leanprover.zulipchat.com/#narrow/channel/287929-mathlib4/topic/inferInstanceAs.20creates.20non-reducible.20diamonds/near/603969174 + npow n x := x ^ n } instance [Add R] [CommMonoid R] (c : RingCon R) : CommMonoid c.Quotient := inferInstanceAs <| CommMonoid c.toCon.Quotient diff --git a/Mathlib/RingTheory/Congruence/Hom.lean b/Mathlib/RingTheory/Congruence/Hom.lean index 29f28b9dcabf2a..a3ad9e08b2f32b 100644 --- a/Mathlib/RingTheory/Congruence/Hom.lean +++ b/Mathlib/RingTheory/Congruence/Hom.lean @@ -12,7 +12,6 @@ public import Mathlib.Algebra.Group.Hom.Defs public import Mathlib.RingTheory.Congruence.Basic public import Mathlib.Algebra.Ring.Subsemiring.Basic public import Mathlib.Algebra.Ring.Subring.Basic -public import Mathlib.Algebra.RingQuot /-! # Congruence relations and ring homomorphisms @@ -80,16 +79,21 @@ theorem comap_eq {g : N →+* M} : c.comap g = ker (c.mk'.comp g) := by rw [ker_comp, ker_mk'_eq] -/-- Makes an isomorphism of quotients by two ring congruence -relations, given that the relations are equal. -/ -protected def congr (h : c = d) : - c.Quotient ≃+* d.Quotient := - { Quotient.congr (Equiv.refl M) <| by apply RingCon.ext_iff.mp h with - map_add' x y := by rcases x with ⟨⟩; rcases y with ⟨⟩; rfl - map_mul' x y := by rcases x with ⟨⟩; rcases y with ⟨⟩; rfl } +/-- An isomorphism of rings `e : M ≃+* N` generates an isomorphism between quotient spaces, +if it is compatible with the relations. -/ +protected def congr {c : RingCon M} {d : RingCon N} (e : M ≃+* N) (h : c = d.comap e) : + c.Quotient ≃+* d.Quotient where + __ := Quotient.congr e <| by apply RingCon.ext_iff.mp h + map_mul' := by rintro ⟨x⟩ ⟨y⟩; exact congrArg toQuotient (e.map_mul x y) + map_add' := by rintro ⟨x⟩ ⟨y⟩; exact congrArg toQuotient (e.map_add x y) -@[simp] theorem congr_mk (h : c = d) (a : M) : - RingCon.congr h (a : c.Quotient) = (a : d.Quotient) := rfl +@[simp] theorem congr_mk {c : RingCon M} {d : RingCon N} (e : M ≃+* N) (h : c = d.comap e) (a : M) : + RingCon.congr e h (a : c.Quotient) = (e a : d.Quotient) := rfl + +@[simp] theorem congr_symm {c : RingCon M} {d : RingCon N} (e : M ≃+* N) (h : c = d.comap e) : + (RingCon.congr e h).symm = + RingCon.congr e.symm (ext <| e.surjective.forall₂.2 <| by simp [h]) := + rfl /-- Given a function `f`, the smallest ring congruence relation containing the binary relation on `f`'s image defined by '`x ≈ y` iff the elements of `f⁻¹(x)` are related to @@ -321,7 +325,7 @@ noncomputable def comapQuotientEquivOfSurj (c : RingCon M) (f : N →+* M) (hf : Function.Surjective f) {d : RingCon N} (hcd : d = c.comap f) : d.Quotient ≃+* c.Quotient := - (RingCon.congr (hcd.trans c.comap_eq)).trans + (RingCon.congr (.refl _) (hcd.trans c.comap_eq)).trans <| RingCon.quotientKerEquivOfSurjective (c.mk'.comp f) (c.mk'_surjective.comp hf) @@ -348,7 +352,7 @@ noncomputable def comapQuotientEquivOfSurj noncomputable def comapQuotientEquivRangeS (f : N →+* M) {d : RingCon N} (hcd : d = comap c f) : d.Quotient ≃+* RingHom.rangeS (c.mk'.comp f) := - (RingCon.congr (hcd.trans comap_eq)).trans <| quotientKerEquivRangeS <| c.mk'.comp f + (RingCon.congr (.refl _) (hcd.trans comap_eq)).trans <| quotientKerEquivRangeS <| c.mk'.comp f @[simp] theorem comapQuotientEquivRangeS_mk (f : N →+* M) {d : RingCon N} (hcd : d = comap c f) (x : N) : @@ -449,15 +453,21 @@ variable {R : Type*} [CommSemiring R] variable {c d : RingCon M} {f : M →ₐ[R] P} variable (R) in -/-- Makes an algebra isomorphism of quotients by two ring congruence -relations, given that the relations are equal. -/ -protected def congrₐ {c d : RingCon M} (h : c = d) : - c.Quotient ≃ₐ[R] d.Quotient := - { RingCon.congr h with - commutes' _ := rfl } - -theorem congrₐ_mk {c d : RingCon M} (h : c = d) (a : M) : - RingCon.congrₐ R h (a : c.Quotient) = (a : d.Quotient) := +/-- An isomorphism of algebras `e : M ≃ₐ[R] N` generates an isomorphism between quotient spaces, +if it is compatible with the relations. -/ +protected def congrₐ {c : RingCon M} {d : RingCon N} (e : M ≃ₐ[R] N) (h : c = d.comap e) : + c.Quotient ≃ₐ[R] d.Quotient where + __ := RingCon.congr e h + commutes' r := by simp [← coe_algebraMap] + +@[simp] +theorem congrₐ_mk {c : RingCon M} {d : RingCon N} (e : M ≃ₐ[R] N) (h : c = d.comap e) (a : M) : + RingCon.congrₐ R e h (a : c.Quotient) = (e a : d.Quotient) := + rfl + +@[simp] theorem congrₐ_symm {c : RingCon M} {d : RingCon N} (e : M ≃ₐ[R] N) (h : c = d.comap e) : + (RingCon.congrₐ R e h).symm = + RingCon.congrₐ R e.symm (ext <| e.surjective.forall₂.2 <| by simp [h]) := rfl theorem range_mkₐ : AlgHom.range (mkₐ R c) = ⊤ := @@ -492,11 +502,20 @@ theorem liftₐ_range (H : c ≤ ker f.toRingHom) : /-- Homomorphisms on the quotient of a ring by a ring congruence relation are equal if they are equal on elements that are coercions from the ring. -/ -@[ext high] -- This should have higher priority than `AlgHom.ext` +-- This should have higher priority than `AlgHom.ext`, but lower than any types implemented with +-- `Quotient`, as `ext` is lax with reducibility. +@[ext 1100] theorem Quotient.hom_extₐ {f g : c.Quotient →ₐ[R] P} (h : f.comp (c.mkₐ R) = g.comp (c.mkₐ R)) : f = g := DFunLike.ext _ _ <| c.mk'_surjective.forall.mpr fun x ↦ by exact congr($h x) +/-- `liftₐ` as an equivalence. -/ +@[simps] +def liftₐEquiv (c : RingCon M) : + { f : M →ₐ[R] P // c ≤ ker (f : M →+* P)} ≃ (c.Quotient →ₐ[R] P) where + toFun f := liftₐ c f.1 f.2 + invFun F := ⟨F.comp (c.mkₐ R), fun x y h => congr(F $(Quotient.sound h))⟩ + variable (f) in /-- The homomorphism induced on the quotient of a ring by the kernel of a ring homomorphism. -/ def kerLiftₐ : (ker f.toRingHom).Quotient →ₐ[R] P := @@ -570,7 +589,7 @@ theorem quotientKerEquivRangeₐ_comp_mkₐ (φ : M →ₐ[R] N) : /-- The **second isomorphism theorem for algebras**. -/ noncomputable def comapQuotientEquivRangeₐ (f : N →ₐ[R] M) {d : RingCon N} (h : d = comap c f) : d.Quotient ≃ₐ[R] AlgHom.range ((c.mkₐ _).comp f) := - (RingCon.congrₐ R (h.trans comap_eq)).trans <| quotientKerEquivRangeₐ ((c.mkₐ _).comp f) + (RingCon.congrₐ R .refl (h.trans comap_eq)).trans <| quotientKerEquivRangeₐ ((c.mkₐ _).comp f) theorem comapQuotientEquivRangeₐ_mk (f : N →ₐ[R] M) {d : RingCon N} (h : d = comap c f) (x : N) : c.comapQuotientEquivRangeₐ f h x = ⟨f x, AlgHom.mem_range_self _ x⟩ := diff --git a/Mathlib/RingTheory/Congruence/Star.lean b/Mathlib/RingTheory/Congruence/Star.lean new file mode 100644 index 00000000000000..b8ed4c30fc26b2 --- /dev/null +++ b/Mathlib/RingTheory/Congruence/Star.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2020 Eric Wieser. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Eric Wieser +-/ +module + +public import Mathlib.RingTheory.Congruence.Basic +public import Mathlib.Algebra.Star.Basic + +/-! +# Helpers for working with star operators on quotients. + +TODO: consider defining `Star` versions of `RingCon`. +-/ + +@[expose] public section + +section Ring +variable {R : Type*} [NonUnitalNonAssocSemiring R] [StarRing R] {r : R → R → Prop} + +theorem RingConGen.Rel.star (hr : ∀ a b, r a b → r (star a) (star b)) + ⦃a b : R⦄ : Rel r a b → Rel r (star a) (star b) + | refl _ => .refl _ + | symm h => .symm <| h.star hr + | trans h1 h2 => .trans (h1.star hr) (h2.star hr) + | of _ _ h => .of _ _ (hr _ _ h) + | mul h1 h2 => by + rw [star_mul, star_mul] + exact (h2.star hr).mul (h1.star hr) + | add h1 h2 => by + rw [star_add, star_add] + exact (h1.star hr).add (h2.star hr) + +theorem ringConGen_star (hr : ∀ a b, r a b → r (star a) (star b)) ⦃a b : R⦄ : + ringConGen r a b → ringConGen r (star a) (star b) := (RingConGen.Rel.star hr ·) + +end Ring diff --git a/Mathlib/RingTheory/Coprime/Lemmas.lean b/Mathlib/RingTheory/Coprime/Lemmas.lean index 438852e8a02a2e..6f47cd085daf75 100644 --- a/Mathlib/RingTheory/Coprime/Lemmas.lean +++ b/Mathlib/RingTheory/Coprime/Lemmas.lean @@ -12,6 +12,7 @@ public import Mathlib.RingTheory.Coprime.Basic /-! # Additional lemmas about elements of a ring satisfying `IsCoprime` + and elements of a monoid satisfying `IsRelPrime` These lemmas are in a separate file to the definition of `IsCoprime` or `IsRelPrime` diff --git a/Mathlib/RingTheory/DedekindDomain/AdicValuation.lean b/Mathlib/RingTheory/DedekindDomain/AdicValuation.lean index 52ceddba6f60ca..f3cb0b5d1c25ae 100644 --- a/Mathlib/RingTheory/DedekindDomain/AdicValuation.lean +++ b/Mathlib/RingTheory/DedekindDomain/AdicValuation.lean @@ -15,6 +15,7 @@ public import Mathlib.RingTheory.Valuation.Discrete.Basic /-! # Adic valuations on Dedekind domains + Given a Dedekind domain `R` of Krull dimension 1 and a maximal ideal `v` of `R`, we define the `v`-adic valuation on `R` and its extension to the field of fractions `K` of `R`. We prove several properties of this valuation, including the existence of uniformizers. diff --git a/Mathlib/RingTheory/DedekindDomain/Different.lean b/Mathlib/RingTheory/DedekindDomain/Different.lean index e0189143a01d15..88a9307503810b 100644 --- a/Mathlib/RingTheory/DedekindDomain/Different.lean +++ b/Mathlib/RingTheory/DedekindDomain/Different.lean @@ -938,8 +938,8 @@ theorem not_dvd_differentIdeal_iff · suffices Algebra.IsSeparable (A ⧸ P.under A) (B ⧸ P) by infer_instance contrapose H exact dvd_differentIdeal_of_not_isSeparable A hp P H - · rw [← Ideal.IsDedekindDomain.ramificationIdx_eq_one_iff hPbot Ideal.map_comap_le] - apply Ideal.ramificationIdx_spec + · rw [← Ideal.IsDedekindDomain.ramificationIdx'_eq_one_iff hPbot Ideal.map_comap_le] + apply Ideal.ramificationIdx'_spec · simp [Ideal.map_le_iff_le_comap] · contrapose H rw [← pow_one P, show 1 = 2 - 1 by simp] @@ -947,8 +947,8 @@ theorem not_dvd_differentIdeal_iff simpa [Ideal.dvd_iff_le] using H · intro H obtain ⟨Q, h₁, h₂⟩ := Ideal.eq_prime_pow_mul_coprime hp' P - rw [← Ideal.IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count hp' ‹_› hPbot, - Ideal.ramificationIdx_eq_one_of_isUnramifiedAt hPbot, pow_one] at h₂ + rw [← Ideal.IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count _ _ hp', + Ideal.ramificationIdx_eq_one_of_isUnramifiedAt, pow_one] at h₂ obtain ⟨h₃, h₄⟩ := (Algebra.isUnramifiedAt_iff_map_eq (p := P.under A) _ _).mp H exact not_dvd_differentIdeal_of_isCoprime_of_isSeparable A P Q (Ideal.isCoprime_iff_sup_eq.mpr h₁) h₂.symm diff --git a/Mathlib/RingTheory/DedekindDomain/Factorization.lean b/Mathlib/RingTheory/DedekindDomain/Factorization.lean index b64b302700dde9..3481ab85dc8195 100644 --- a/Mathlib/RingTheory/DedekindDomain/Factorization.lean +++ b/Mathlib/RingTheory/DedekindDomain/Factorization.lean @@ -5,7 +5,7 @@ Authors: María Inés de Frutos-Fernández -/ module -public import Mathlib.NumberTheory.RamificationInertia.Basic +public import Mathlib.RingTheory.RamificationInertia.Basic public import Mathlib.Order.Filter.Cofinite public import Mathlib.RingTheory.UniqueFactorizationDomain.Finsupp @@ -819,7 +819,7 @@ If `p` is a maximal ideal, then the lift of `p` in an extension is the product o over `p` to the power the ramification index. -/ theorem Ideal.map_algebraMap_eq_finsetProd_pow {p : Ideal S} [p.IsMaximal] (hp : p ≠ 0) : - map (algebraMap S R) p = ∏ P ∈ p.primesOver R, P ^ p.ramificationIdx P := by + map (algebraMap S R) p = ∏ P ∈ p.primesOver R, P ^ P.ramificationIdx S := by classical have h : map (algebraMap S R) p ≠ 0 := map_ne_bot_of_ne_bot hp rw [← finprod_heightOneSpectrum_factorization (I := p.map (algebraMap S R)) h] @@ -830,8 +830,8 @@ theorem Ideal.map_algebraMap_eq_finsetProd_pow {p : Ideal S} [p.IsMaximal] (hp : ← Finset.prod_set_coe] · let _ : Fintype {v : HeightOneSpectrum R // v.asIdeal ∣ map (algebraMap S R) p} := hF refine Fintype.prod_equiv (equivPrimesOver _ hp) _ _ fun ⟨v, _⟩ ↦ ?_ - simp [maxPowDividing_eq_pow_multiset_count _ h, - ramificationIdx_eq_factors_count h v.isPrime v.ne_bot] + have : v.asIdeal.LiesOver p := by rwa [Ideal.liesOver_iff_dvd_map v.2.ne_top] + simp [maxPowDividing_eq_pow_multiset_count _ h, ramificationIdx_eq_factors_count p v h] · intro v hv simpa [maxPowDividing, Function.mem_mulSupport, IsPrime.ne_top _, Associates.count_ne_zero_iff_dvd h (irreducible v)] using hv diff --git a/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean b/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean index 98386a33a30df8..7f5ce30facd4e0 100644 --- a/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean +++ b/Mathlib/RingTheory/DedekindDomain/FiniteAdeleRing.lean @@ -12,6 +12,7 @@ public import Mathlib.Topology.Algebra.RestrictedProduct.Units /-! # The finite adèle ring of a Dedekind domain + We define the ring of finite adèles of a Dedekind domain `R`. ## Main definitions diff --git a/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean b/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean index edbb53bbc80601..9ee3f36a59bbc5 100644 --- a/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean +++ b/Mathlib/RingTheory/DedekindDomain/Ideal/Lemmas.lean @@ -744,7 +744,7 @@ def normalizedFactorsEquivOfQuotEquiv (hI : I ≠ ⊥) (hJ : J ≠ ⊥) : idealFactorsEquivOfQuotEquiv_mem_normalizedFactors_of_mem_normalizedFactors f.symm hI j.prop⟩ left_inv := fun ⟨j, hj⟩ => by simp - right_inv := fun ⟨j, hj⟩ => by simp [-Set.coe_setOf] + right_inv := fun ⟨j, hj⟩ => by simp @[deprecated (since := "2026-04-16")] alias _root_.normalizedFactorsEquivOfQuotEquiv := normalizedFactorsEquivOfQuotEquiv diff --git a/Mathlib/RingTheory/DedekindDomain/Instances.lean b/Mathlib/RingTheory/DedekindDomain/Instances.lean index 982a7cf3602f09..87a52e52b08453 100644 --- a/Mathlib/RingTheory/DedekindDomain/Instances.lean +++ b/Mathlib/RingTheory/DedekindDomain/Instances.lean @@ -11,6 +11,7 @@ public import Mathlib.RingTheory.RingHom.Finite /-! # Instances for Dedekind domains + This file contains various instances to work with localization of a ring extension. A very common situation in number theory is to have an extension of (say) Dedekind domains `R` and diff --git a/Mathlib/RingTheory/Depth/Rees.lean b/Mathlib/RingTheory/Depth/Rees.lean new file mode 100644 index 00000000000000..302d5394afc6e9 --- /dev/null +++ b/Mathlib/RingTheory/Depth/Rees.lean @@ -0,0 +1,184 @@ +/- +Copyright (c) 2025 Nailin Guan. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Nailin Guan +-/ +module + +public import Mathlib.Algebra.Category.Grp.Zero +public import Mathlib.Algebra.Category.ModuleCat.Ext.Basic +public import Mathlib.RingTheory.Regular.Category +public import Mathlib.RingTheory.Regular.LinearMap +public import Mathlib.RingTheory.Regular.RegularSequence +public import Mathlib.RingTheory.Spectrum.Prime.Topology + +/-! + +# The Rees theorem + +In this file we prove the Rees theorem for depth, which relates the vanishing of +certain `Ext` groups and the length of a maximal regular sequence in a certain ideal. + +## Main results + +* `ModuleCat.exists_isRegular_tfae` (Rees theorem) : For any `n : ℕ`, Noetherian ring `R`, + `I : Ideal R`, and finitely generated and nontrivial `R`-module `M` satisfying `IM < M`, + the following are equivalent: + · for any `N : ModuleCat R` finitely generated such that `Supp N ⊆ V(I)`, `∀ i < n, Ext N M i = 0` + · `∀ i < n, Ext (R ⧸ I) M i = 0` + · there exists a `N : ModuleCat R` finitely generated and nontrivial with `Supp N = V(I)` + such that `∀ i < n, Ext N M i = 0` + · there exists a `M`-regular sequence of length `n` with every element in `I` + +## References + +* [Commutative Algebra, Theorem 28][matsumuraCommAlg] + +-/ + +public section + +universe v u + +open LinearMap RingTheory.Sequence Ideal CategoryTheory Abelian Limits Pointwise IsSMulRegular + +variable {R : Type u} [CommRing R] + +private lemma smul_top_quotSMulTop_ne_top_of_smul_top_ne_top {M : Type*} [AddCommGroup M] + [Module R M] {I : Ideal R} {r : R} (hr : r ∈ I) + (hI : I • (⊤ : Submodule R M) ≠ ⊤) : + I • (⊤ : Submodule R (QuotSMulTop r M)) ≠ ⊤ := by + by_contra eq + absurd congrArg (Submodule.comap (Submodule.mkQ _)) eq + simpa [Submodule.comap_smul_top_of_surjective I _ (Submodule.mkQ_surjective _), + Submodule.smul_mono_left ((span_singleton_le_iff_mem I).mpr hr), + ← Submodule.ideal_span_singleton_smul] using hI + +namespace ModuleCat + +/-- The implication `(3) → (4)` of `exists_isRegular_tfae`: for `M N` finitely generated +module over Noetherian ring `R` and ideal `I` satisfying `IM < M` and `Supp N = V(I)`, +if `Ext N M i = 0` for all `i < n`, +then there exists an `M`-regular sequence of length `n` contained in `I`. -/ +lemma exists_isRegular_of_exists_subsingleton_ext [Small.{v} R] [IsNoetherianRing R] (I : Ideal R) + (n : ℕ) (M : ModuleCat.{v} R) [Module.Finite R M] (smul_lt : I • (⊤ : Submodule R M) < ⊤) + (N : ModuleCat.{v} R) [Module.Finite R N] + (h_supp : Module.support R N = PrimeSpectrum.zeroLocus I) + (h_ext : ∀ i < n, Subsingleton (Ext N M i)) : + ∃ rs : List R, rs.length = n ∧ (∀ r ∈ rs, r ∈ I) ∧ IsRegular M rs := by + induction n generalizing M with + | zero => + have : Nontrivial M := (Submodule.nontrivial_iff R).mp (nontrivial_of_lt _ _ smul_lt) + use [] + simp [isRegular_iff] + | succ n ih => + rw [Module.support_eq_zeroLocus, PrimeSpectrum.zeroLocus_eq_iff] at h_supp + -- use `Ext N M 0` vanish to obtain an `M`-regular element `x` in `Ann(N)` + have : Subsingleton (N ⟶ M) := Ext.addEquiv₀.subsingleton_congr.mp (h_ext 0 n.zero_lt_succ) + have : Subsingleton (N →ₗ[R] M) := ModuleCat.homAddEquiv.symm.subsingleton + obtain ⟨x, mem_ann, hx⟩ := subsingleton_linearMap_iff.mp this + -- take a power of it to make `xᵏ` fall into `I` + obtain ⟨k, hk⟩ := le_of_le_of_eq Ideal.le_radical h_supp mem_ann + -- verify that `N` indeed make `M ⧸ xᵏM` satisfy the induction hypothesis + have h_ext' : ∀ i < n, Subsingleton (Ext N (ModuleCat.of R (QuotSMulTop (x ^ k) M)) i) := by + intro i hi + -- the vanishing of `Ext` is obtained from the (covariant) long exact sequence given by + -- `M.smulShortComplex (x ^ k)` + have zero1 := AddCommGrpCat.isZero_of_iff_subsingleton.mpr (h_ext i (by omega)) + have zero2 := AddCommGrpCat.isZero_of_iff_subsingleton.mpr (h_ext (i + 1) (by omega)) + exact AddCommGrpCat.subsingleton_of_isZero <| ShortComplex.Exact.isZero_of_both_zeros + ((Ext.covariant_sequence_exact₃' N (hx.pow k).smulShortComplex_shortExact) i (i + 1) rfl) + (zero1.eq_zero_of_src _) (zero2.eq_zero_of_tgt _) + obtain ⟨rs, len, mem, reg⟩ := ih (ModuleCat.of R (QuotSMulTop (x ^ k) M)) + (smul_top_quotSMulTop_ne_top_of_smul_top_ne_top hk smul_lt.ne).lt_top h_ext' + use x ^ k :: rs + simpa [len, hk] using ⟨mem, hx.pow k, reg⟩ + +/-- The implication `(4) → (1)` of `exists_isRegular_tfae`: for `M N` finitely generated +module over Noetherian ring `R` and ideal `I` satisfying `IM < M` and `Supp N ⊆ V(I)`, +if there is an `M`-regular sequence `rs` contained in `I`, +then `Ext N M i = 0` for all `i < rs.length`. -/ +lemma subsingleton_ext_of_exists_isRegular [Small.{v} R] [IsNoetherianRing R] (I : Ideal R) + (N : ModuleCat.{v} R) [Nfin : Module.Finite R N] + (Nsupp : Module.support R N ⊆ PrimeSpectrum.zeroLocus I) + (M : ModuleCat.{v} R) [Module.Finite R M] (smul_lt : I • (⊤ : Submodule R M) < ⊤) + (rs : List R) (mem : ∀ r ∈ rs, r ∈ I) (reg : IsRegular M rs) : + ∀ i < rs.length, Subsingleton (Ext N M i) := by + generalize len : rs.length = n + induction n generalizing M rs with + | zero => simp + | succ n ih => + rintro i hi + have le_rad := Nsupp + rw [Module.support_eq_zeroLocus, PrimeSpectrum.zeroLocus_subset_zeroLocus_iff] at le_rad + match rs with + | [] => simp at len + | a :: rs' => + -- find a positive power of `a` lying in `Ann(N)` + obtain ⟨k, hk⟩ := le_rad (mem a List.mem_cons_self) + simp only [isRegular_cons_iff] at reg + simp only [List.mem_cons, forall_eq_or_imp] at mem + simp only [List.length_cons, Nat.add_left_inj] at len + -- prepare to apply induction hypothesis to `M/aM` + match i with + | 0 => -- vanishing of `Ext N M 0` follows from `aᵏ ∈ Ann(N)` + have : Subsingleton (N →ₗ[R] M) := subsingleton_linearMap_iff.mpr ⟨a ^ k, hk, reg.1.pow k⟩ + exact (Ext.addEquiv₀.trans ModuleCat.homAddEquiv).subsingleton + | i + 1 => + let g := (AddCommGrpCat.ofHom ((Ext.mk₀ (smulShortComplex M a).f).postcomp N + (add_zero (i + 1)))) + -- from the (covariant) long exact sequence given by `M.smulShortComplex a` + -- we obtain scalar multiple by `a` on `Ext N M i` is injective + have mono_g : Mono g := by + apply (Ext.covariant_sequence_exact₁' N reg.1.smulShortComplex_shortExact i (i + 1) + rfl).mono_g ((AddCommGrpCat.isZero_of_iff_subsingleton.mpr ?_).eq_zero_of_src _) + apply ih (ModuleCat.of R (QuotSMulTop a M)) _ rs' mem.2 reg.2 len i (by omega) + exact (smul_top_quotSMulTop_ne_top_of_smul_top_ne_top mem.1 smul_lt.ne).lt_top + let gk := AddCommGrpCat.ofHom ((Ext.mk₀ (M.smulShortComplex (a ^ k)).f).postcomp N + (add_zero (i + 1))) + have mono_gk : Mono gk := by + simp only [smulShortComplex_f_eq_smul_id, g, gk] at mono_g ⊢ + exact (Ext.postcomp_smul_id_mono_iff (a ^ k) (i + 1)).mpr <| + ((Ext.postcomp_smul_id_mono_iff a (i + 1)).mp mono_g).pow k + -- scalar multiple by `aᵏ` on `Ext N M i` is zero since `aᵏ ∈ Ann(N)`, so `Ext N M i` vanish + have zero_gk : gk = 0 := Ext.postcomp_smul_id_eq_zero_of_mem_annihilator hk (i + 1) + exact AddCommGrpCat.subsingleton_of_isZero (IsZero.of_mono_eq_zero _ zero_gk) + +/-- +**The Rees theorem** +For any `n : ℕ`, Noetherian ring `R`, `I : Ideal R`, and finitely generated and nontrivial +`R`-module `M` satisfying `IM < M`, the following are equivalent: +* for any `N : ModuleCat R` finitely generated and nontrivial with support contained in the + zero locus of `I`, `∀ i < n, Ext N M i = 0` +* `∀ i < n, Ext (R ⧸ I) M i = 0` +* there exists a `N : ModuleCat R` finitely generated and nontrivial with support equal to the + zero locus of `I`, `∀ i < n, Ext N M i = 0` +* there exists a `M`-regular sequence of length `n` with every element in `I` +-/ +lemma exists_isRegular_tfae [Small.{v} R] [IsNoetherianRing R] (I : Ideal R) (n : ℕ) + (M : ModuleCat.{v} R) [Module.Finite R M] (smul_lt : I • (⊤ : Submodule R M) < ⊤) : + [∀ N : ModuleCat.{v} R, Nontrivial N → Module.Finite R N → + Module.support R N ⊆ PrimeSpectrum.zeroLocus I → ∀ i < n, Subsingleton (Ext N M i), + ∀ i < n, Subsingleton (Ext (ModuleCat.of R (Shrink.{v} (R ⧸ I))) M i), + ∃ N : ModuleCat R, Nontrivial N ∧ Module.Finite R N ∧ + Module.support R N = PrimeSpectrum.zeroLocus I ∧ ∀ i < n, Subsingleton (Ext N M i), + ∃ rs : List R, rs.length = n ∧ (∀ r ∈ rs, r ∈ I) ∧ RingTheory.Sequence.IsRegular M rs + ].TFAE := by + -- two main implications `3 → 4` and `4 → 1` are separated out, the rest are trivial + have ntrQ : Nontrivial (R ⧸ I) := by + apply Submodule.Quotient.nontrivial_iff.mpr + by_contra eq + simp [eq] at smul_lt + have suppQ : Module.support R (Shrink.{v} (R ⧸ I)) = PrimeSpectrum.zeroLocus I := by + rw [(Shrink.linearEquiv R _).support_eq, Module.support_eq_zeroLocus, annihilator_quotient] + tfae_have 1 → 2 := fun h1 i hi ↦ h1 (ModuleCat.of R (Shrink.{v} (R ⧸ I))) + inferInstance inferInstance suppQ.subset i hi + tfae_have 2 → 3 := fun h2 ↦ ⟨(ModuleCat.of R (Shrink.{v} (R ⧸ I))), + inferInstance, Module.Finite.equiv (Shrink.linearEquiv R (R ⧸ I)).symm, suppQ, h2⟩ + tfae_have 3 → 4 := fun ⟨N, _, _, h_supp, h_ext⟩ ↦ + exists_isRegular_of_exists_subsingleton_ext I n M smul_lt N h_supp h_ext + tfae_have 4 → 1 := fun ⟨rs, len, mem, reg⟩ N Nntr Nfin Nsupp i hi ↦ + subsingleton_ext_of_exists_isRegular I N Nsupp M smul_lt rs mem reg i (hi.trans_eq len.symm) + tfae_finish + +end ModuleCat diff --git a/Mathlib/RingTheory/Derivation/DifferentialRing.lean b/Mathlib/RingTheory/Derivation/DifferentialRing.lean index b1d7fb96ad0c88..94b6c54691de99 100644 --- a/Mathlib/RingTheory/Derivation/DifferentialRing.lean +++ b/Mathlib/RingTheory/Derivation/DifferentialRing.lean @@ -93,8 +93,4 @@ lemma DifferentialAlgebra.equiv {A : Type*} [CommRing A] [Differential A] letI := Differential.equiv h.toRingEquiv ⟨fun a ↦ by change (LinearMap.comp ..) _ = _ - simp only [RingHom.toAddMonoidHom_eq_coe, - RingEquiv.toRingHom_eq_coe, AlgEquiv.toRingEquiv_toRingHom, LinearMap.coe_comp, - AddMonoidHom.coe_toIntLinearMap, AddMonoidHom.coe_coe, RingHom.coe_coe, Derivation.coeFn_coe, - Function.comp_apply, AlgEquiv.commutes, deriv_algebraMap] - apply h.symm.commutes⟩ + simp [deriv_algebraMap]⟩ diff --git a/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean b/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean index b5da30718aae74..c36b5a97e98dfb 100644 --- a/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean +++ b/Mathlib/RingTheory/DividedPowerAlgebra/Init.lean @@ -6,7 +6,8 @@ Authors: Antoine Chambert-Loir, María Inés de Frutos—Fernández module public import Mathlib.Algebra.MvPolynomial.Eval -public import Mathlib.Algebra.RingQuot +public import Mathlib.RingTheory.Congruence.Hom +public import Mathlib.RingTheory.Congruence.BigOperators public import Mathlib.RingTheory.DividedPowers.Basic /-! @@ -25,7 +26,7 @@ divided powers (`DividedPowerAlgebra.lift`). divided power algebra. * `DividedPowerAlgebra R M`: the universal divided power algebra of the `R`-module `M`, - defined as `RingQuot` of `DividedPowerAlgebra.Rel R M`. + defined as `RingCon.Quotient` of `DividedPowerAlgebra.ringCon R M`. * `DividedPowerAlgebra.dp R n m`: for `n : ℕ` and `m : M`, this is the equivalence class of `MvPolynomial.X (⟨n, m⟩)` in `DividedPowerAlgebra R M`. @@ -68,9 +69,9 @@ divided powers (`DividedPowerAlgebra.lift`). noncomputable section -open Finset Ideal MvPolynomial RingQuot +open Finset Ideal MvPolynomial -variable (R M : Type*) [CommSemiring R] [AddCommMonoid M] [Module R M] +variable (α R M : Type*) [CommSemiring R] [AddCommMonoid M] [Module R M] namespace DividedPowerAlgebra @@ -87,13 +88,16 @@ inductive Rel : MvPolynomial (ℕ × M) R → MvPolynomial (ℕ × M) R → Prop /-- The ideal of `MvPolynomial (ℕ × M) R` generated by `Rel`. -/ def RelI : Ideal (MvPolynomial (ℕ × M) R) := ofRel (DividedPowerAlgebra.Rel R M) +/-- The congruence generated by `Rel`. -/ +def ringCon : RingCon (MvPolynomial (ℕ × M) R) := ringConGen (DividedPowerAlgebra.Rel R M) + end DividedPowerAlgebra /-- The divided power algebra of a module M is defined as the ring quotient of the polynomial ring in the variables `ℕ × M` by the ring relation defined by `DividedPowerAlgebra.Rel`. We will later show that that `DividedPowerAlgebra R M` has divided powers. It satisfies a weak universal property for morphisms to rings with divided powers. -/ -abbrev DividedPowerAlgebra := RingQuot (DividedPowerAlgebra.Rel R M) +abbrev DividedPowerAlgebra := DividedPowerAlgebra.ringCon R M |>.Quotient namespace DividedPowerAlgebra @@ -101,59 +105,58 @@ open MvPolynomial variable {R M} -lemma mkAlgHom_surjective : Function.Surjective (mkAlgHom R (Rel R M)) := - RingQuot.mkAlgHom_surjective _ _ +lemma mkAlgHom_surjective : Function.Surjective (RingCon.mkₐ R (ringCon R M)) := + Quotient.mk_surjective + +@[simp] +lemma coe_C (a : R) : + ↑(C (σ := ℕ × M) a) = algebraMap R (DividedPowerAlgebra R M) a := by + rw [← MvPolynomial.algebraMap_eq, RingCon.coe_algebraMap] +@[deprecated coe_C (since := "2026-06-19")] lemma mkAlgHom_C (a : R) : - mkAlgHom R (Rel R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a := by + RingCon.mkₐ R (ringCon R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a := by rw [← MvPolynomial.algebraMap_eq, AlgHom.commutes] +@[deprecated coe_C (since := "2026-06-19")] lemma mkRingHom_C (a : R) : - mkRingHom (Rel R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a := by - rw [← mkAlgHom_C, mkAlgHom, AlgHom.coe_mk] + RingCon.mk' (ringCon R M) (C a) = algebraMap R (DividedPowerAlgebra R M) a := + mkAlgHom_C _ variable (R) in /-- `dp R n m` is the equivalence class of `X (⟨n, m⟩)` in `DividedPowerAlgebra R M`. -/ -def dp (n : ℕ) (m : M) : DividedPowerAlgebra R M := mkAlgHom R (Rel R M) (X ⟨n, m⟩) +def dp (n : ℕ) (m : M) : DividedPowerAlgebra R M := ↑(X (n, m) : MvPolynomial (ℕ × M) R) theorem dp_def (n : ℕ) (m : M) : - dp R n m = mkAlgHom R (Rel R M) (X ⟨n, m⟩) := rfl + dp R n m = ↑(X (n, m) : MvPolynomial (ℕ × M) R) := rfl protected theorem induction_on' {P : DividedPowerAlgebra R M → Prop} (f : DividedPowerAlgebra R M) - (h_C : ∀ a, P (mkAlgHom R (Rel R M) (C a))) (h_add : ∀ f g, P f → P g → P (f + g)) + (h_C : ∀ a, P (C a : MvPolynomial (ℕ × M) R)) (h_add : ∀ f g, P f → P g → P (f + g)) (h_dp : ∀ (f : DividedPowerAlgebra R M) (n : ℕ) (m : M), P f → P (f * dp R n m)) : P f := by - obtain ⟨F, hf⟩ := RingQuot.mkRingHom_surjective (DividedPowerAlgebra.Rel R M) f - rw [← hf] - induction F using MvPolynomial.induction_on generalizing f with - | C a => - convert! h_C a using 1 - rw [mkAlgHom, AlgHom.coe_mk] + induction f using Quot.induction_on with | _ F + dsimp + induction F using MvPolynomial.induction_on with + | C a => exact h_C a | add g1 g2 hg1 hg2 => - rw [map_add] - exact h_add _ _ (hg1 ((mkRingHom (Rel R M)) g1) rfl) (hg2 ((mkRingHom (Rel R M)) g2) rfl) + rw [RingCon.coe_add] + exact h_add _ _ hg1 hg2 | mul_X g nm h => - have h' : (mkRingHom (Rel R M)) (X nm) = dp R nm.1 nm.2 := by - simp only [dp_def, Prod.mk.eta, mkAlgHom, AlgHom.coe_mk] - rw [_root_.map_mul, h'] - exact h_dp _ _ _ (h (mkRingHom (Rel R M) g) rfl) + rw [RingCon.coe_mul] + exact h_dp _ _ _ h @[elab_as_elim] protected theorem induction_on {P : DividedPowerAlgebra R M → Prop} (f : DividedPowerAlgebra R M) (C : ∀ a, P (algebraMap R _ a)) (add : ∀ f g, P f → P g → P (f + g)) (dp : ∀ (f : DividedPowerAlgebra R M) (n : ℕ) (m : M), P f → P (f * dp R n m)) : P f := - DividedPowerAlgebra.induction_on' f (fun a ↦ by rw [mkAlgHom_C]; exact C a) add dp - -theorem dp_eq_mkRingHom (n : ℕ) (m : M) : - dp R n m = mkRingHom (Rel R M) (X (⟨n, m⟩)) := by - simp [dp, mkRingHom, mkAlgHom] + DividedPowerAlgebra.induction_on' f C add dp theorem dp_zero {m : M} : dp R 0 m = 1 := by - rw [dp_def, ← map_one (mkAlgHom R (Rel R M))] - exact RingQuot.mkAlgHom_rel R Rel.zero + rw [dp_def, ← RingCon.coe_one] + exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.zero theorem dp_smul {r : R} {n : ℕ} {m : M} : dp R n (r • m) = r ^ n • dp R n m := by - rw [dp_def, dp_def, ← map_smul] - exact mkAlgHom_rel R Rel.smul + rw [dp_def, dp_def, ← RingCon.coe_smul] + exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.smul theorem dp_null {n : ℕ} : dp R n (0 : M) = if n = 0 then 1 else 0 := by cases Nat.eq_zero_or_pos n with @@ -168,14 +171,13 @@ theorem dp_null_of_ne_zero {n : ℕ} (hn : n ≠ 0) : dp R n (0 : M) = 0 := by theorem dp_mul {n p : ℕ} {m : M} : dp R n m * dp R p m = (n + p).choose n • dp R (n + p) m := by - simp only [dp_def, ← _root_.map_mul, ← map_nsmul] - exact mkAlgHom_rel R Rel.mul + simp only [dp_def, ← RingCon.coe_mul, ← RingCon.coe_nsmul] + exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.mul theorem dp_add {n : ℕ} {x y : M} : dp R n (x + y) = (antidiagonal n).sum fun k ↦ dp R k.1 x * dp R k.2 y := by - simp only [dp_def] - rw [mkAlgHom_rel (A := MvPolynomial (ℕ × M) R) R Rel.add, map_sum, - Finset.sum_congr rfl (fun k _ ↦ by rw [_root_.map_mul])] + simp_rw [dp_def, ← RingCon.coe_mul, ← RingCon.coe_finsetSum] + exact Quotient.sound <| RingCon.le_ringConGen _ _ Rel.add theorem dp_sum {ι : Type*} [DecidableEq ι] (s : Finset ι) (q : ℕ) (x : ι → M) : dp R q (s.sum x) = @@ -317,7 +319,9 @@ def lift' {f : ℕ × M → A} (hf_zero : ∀ m, f (0, m) = 1) (hf_mul : ∀ n p m, f ⟨n, m⟩ * f ⟨p, m⟩ = (n + p).choose n • f ⟨n + p, m⟩) (hf_add : ∀ n u v, f ⟨n, u + v⟩ = (antidiagonal n).sum fun (k, l) ↦ f ⟨k, u⟩ * f ⟨l, v⟩) : DividedPowerAlgebra R M →ₐ[R] A := - RingQuot.liftAlgHom R ⟨eval₂AlgHom R f, by exact lift'_imp R M hf_zero hf_smul hf_mul hf_add⟩ + RingCon.liftₐ _ (eval₂AlgHom R f) <| by + grw [ringCon, RingCon.ringConGen_le] + exact lift'_imp R M hf_zero hf_smul hf_mul hf_add @[simp] theorem lift'_apply {f : ℕ × M → A} (hf_zero : ∀ m, f (0, m) = 1) @@ -325,7 +329,7 @@ theorem lift'_apply {f : ℕ × M → A} (hf_zero : ∀ m, f (0, m) = 1) (hf_mul : ∀ n p m, f ⟨n, m⟩ * f ⟨p, m⟩ = (n + p).choose n • f ⟨n + p, m⟩) (hf_add : ∀ n u v, f ⟨n, u + v⟩ = (antidiagonal n).sum fun (k, l) ↦ f ⟨k, u⟩ * f ⟨l, v⟩) (p : MvPolynomial (ℕ × M) R) : - lift' hf_zero hf_smul hf_mul hf_add (mkAlgHom R (Rel R M) p) = aeval f p := by + lift' hf_zero hf_smul hf_mul hf_add ↑p = aeval f p := by simp [lift', aeval_eq_eval₂Hom] @[simp] @@ -354,7 +358,7 @@ variable {g} @[simp] theorem lift_apply (p : MvPolynomial (ℕ × M) R) : - lift hI g hg (mkAlgHom R (Rel R M) p) = aeval (fun nm : ℕ × M ↦ hI.dpow nm.1 (g nm.2)) p := by + lift hI g hg ↑p = aeval (fun nm : ℕ × M ↦ hI.dpow nm.1 (g nm.2)) p := by rw [lift, lift'_apply] @[simp] @@ -421,7 +425,7 @@ def map : DividedPowerAlgebra R M →ₐ[R] DividedPowerAlgebra S N := @[simp] theorem map_apply {p : MvPolynomial (ℕ × M) R} : - map S f (mkAlgHom R (Rel R M) p) = aeval (fun nm ↦ dp S nm.fst (f nm.snd)) p := by + map S f ↑p = aeval (fun nm ↦ dp S nm.fst (f nm.snd)) p := by rw [map, lift'_apply] @[simp] @@ -439,7 +443,7 @@ theorem lift_comp_embed : theorem lift_surjective {f : M →ₗ[R] N} (hf : Function.Surjective f) : Function.Surjective (map R f) := by rw [← AlgHom.range_eq_top, ← Algebra.map_top (map R f), eq_top_iff, - ← (AlgHom.range_eq_top (mkAlgHom R (Rel R N))).mpr mkAlgHom_surjective, + ← (AlgHom.range_eq_top (RingCon.mkₐ R (ringCon R N))).mpr mkAlgHom_surjective, ← Algebra.map_top, (Subalgebra.gc_map_comap _).le_iff_le, ← MvPolynomial.adjoin_range_X, Algebra.adjoin_le_iff] intro @@ -447,7 +451,7 @@ theorem lift_surjective {f : M →ₗ[R] N} (hf : Function.Surjective f) : rintro ⟨n, m, rfl⟩ obtain ⟨l, rfl⟩ := hf m simp only [Algebra.map_top, Subalgebra.coe_comap, AlgHom.coe_range, Set.mem_preimage, - Set.mem_range] + Set.mem_range, RingCon.mkₐ_apply] use dp R n l rw [map_apply_dp, dp] @@ -491,11 +495,11 @@ theorem LinearEquiv.coe_lift_symm (g : M ≃ₗ[R] N) : (mapEquiv g).symm = map R g.symm.toLinearMap := rfl theorem mapEquiv_refl : mapEquiv (LinearEquiv.refl R M) = AlgEquiv.refl := - AlgEquiv.coe_algHom_injective map_id + AlgEquiv.coe_toAlgHom_injective map_id theorem mapEquiv_trans (g : M ≃ₗ[R] N) (h : N ≃ₗ[R] P) : (mapEquiv g).trans (mapEquiv h) = mapEquiv (g.trans h) := - AlgEquiv.coe_algHom_injective (map_comp _ _).symm + AlgEquiv.coe_toAlgHom_injective (map_comp _ _).symm end IsScalarTower diff --git a/Mathlib/RingTheory/Etale/Basic.lean b/Mathlib/RingTheory/Etale/Basic.lean index 1277c23b518a03..c941952589073a 100644 --- a/Mathlib/RingTheory/Etale/Basic.lean +++ b/Mathlib/RingTheory/Etale/Basic.lean @@ -130,6 +130,7 @@ lemma _root_.Algebra.FormallySmooth.iff_restrictScalars [FormallyEtale R A] : Algebra.FormallySmooth R B ↔ Algebra.FormallySmooth A B := ⟨fun _ ↦ .of_restrictScalars R _ _, fun _ ↦ .comp _ A _⟩ +set_option linter.dupNamespace false in @[deprecated (since := "2025-12-09")] alias Algebra.FormallyEtale.of_restrictScalars := of_restrictScalars @@ -142,6 +143,7 @@ lemma iff_of_surjective rw [FormallyEtale.iff_formallyUnramified_and_formallySmooth, ← FormallySmooth.iff_of_surjective h, and_iff_right (FormallyUnramified.of_surjective (Algebra.ofId R S) h)] +set_option linter.dupNamespace false in @[deprecated (since := "2025-12-09")] alias Algebra.FormallyEtale.iff_of_surjective := iff_of_surjective diff --git a/Mathlib/RingTheory/Etale/Finite.lean b/Mathlib/RingTheory/Etale/Finite.lean new file mode 100644 index 00000000000000..18b0bc0c2ac738 --- /dev/null +++ b/Mathlib/RingTheory/Etale/Finite.lean @@ -0,0 +1,193 @@ +/- +Copyright (c) 2026 Christian Merten. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Christian Merten +-/ +module + +public import Mathlib.Algebra.Category.CommAlgCat.Basic +public import Mathlib.CategoryTheory.FintypeCat +public import Mathlib.RingTheory.TotallySplit + +/-! +# Category of finite étale `R`-algebras + +In this file we define the category of finite étale `R`-algebras over a ring `R`. For any +geometric point `Ω` of `R`, we define a fiber functor sending a finite étale `R`-algebra +`S` to the finite set of `R`-algebra homomorphisms `S →ₐ[R] Ω`. + +## Main definitions + +- `CommAlgCat.FiniteEtale`: The category of finite étale `R`-algebras. +- `CommAlgCat.FiniteEtale.fiber`: For a geometric point `Ω` of `R`, the fiber functor + `S ↦ (S →ₐ[R] Ω)`. + +## Main results + +- `CommAlgCat.FiniteEtale.equivOfIsSepClosed`: If `R = Ω` is separably closed, + the category of finite étale `Ω`-algebras is anti-equivalent to `FintypeCat`. + In particular, the functor `CommAlgCat.FiniteEtale.fiber` is an equivalence + of categories in this case. +-/ + +public section + +open CategoryTheory TensorProduct + +universe v w u + +namespace CommAlgCat + +variable (R : Type u) [CommRing R] (k : Type u) [Field k] + +section + +/-- The object property of finite `R`-algebras. -/ +abbrev finite : ObjectProperty (CommAlgCat.{v} R) := + fun S ↦ Module.Finite R S + +/-- The object property of étale `R`-algebras. -/ +abbrev etale : ObjectProperty (CommAlgCat.{v} R) := + fun S ↦ Algebra.Etale R S + +/-- The object property of finite étale `R`-algebras. -/ +abbrev finiteEtale : ObjectProperty (CommAlgCat.{v} R) := + finite R ⊓ etale R + +/-- The category of finite étale `R`-algebras. -/ +abbrev FiniteEtale (R : Type u) [CommRing R] : Type _ := + (finiteEtale.{v} R).FullSubcategory + +instance : CoeSort (FiniteEtale.{v} R) (Type v) := ⟨fun R ↦ R.obj⟩ + +instance (S : FiniteEtale.{v} R) : Algebra.Etale R S := + S.property.right + +instance (S : FiniteEtale.{v} R) : Module.Finite R S := + S.property.left + +/-- Construct a term of `FiniteEtale R` from a finite étale `R`-algebra. -/ +@[simps obj] +abbrev FiniteEtale.of (S : Type v) [CommRing S] [Algebra R S] + [Module.Finite R S] [Algebra.Etale R S] : + FiniteEtale.{v} R where + obj := .of R S + property := ⟨‹_›, ‹_›⟩ + +variable {R} + +/-- Construct a morphism in `FiniteEtale R` from an algebra map. -/ +@[simps] +abbrev FiniteEtale.ofHom {S T : Type v} [CommRing S] [CommRing T] + [Algebra R S] [Algebra R T] [Module.Finite R S] [Algebra.Etale R S] [Module.Finite R T] + [Algebra.Etale R T] (f : S →ₐ[R] T) : + FiniteEtale.of R S ⟶ FiniteEtale.of R T where + hom := CommAlgCat.ofHom f + +/-- Construct an isomorphism in `FiniteEtale R` from an algebra equivalence. -/ +abbrev FiniteEtale.isoMk {S T : FiniteEtale R} (e : S.obj ≃ₐ[R] T.obj) : + S ≅ T := + ObjectProperty.isoMk _ (CommAlgCat.isoMk e) + +end + +instance (R : FiniteEtale k) : IsArtinianRing R := + have := Algebra.FormallyUnramified.finite_of_free k R + isArtinian_of_tower k inferInstance + +variable (Ω : Type w) [Field Ω] [Algebra R Ω] + (S : Type w) [CommRing S] [Algebra R S] [Algebra S Ω] [IsScalarTower R S Ω] + +/-- If `S` is an `R`-algebra, this is the base change functor `A ↦ S ⊗[R] A`. -/ +@[expose, simps] +def FiniteEtale.baseChange : FiniteEtale.{v} R ⥤ FiniteEtale.{max w v} S where + obj A := .of S (S ⊗[R] A) + map {A B} f := FiniteEtale.ofHom (Algebra.TensorProduct.map (.id _ _) f.hom.hom) + +/-- Base change from `R` to `R` is isomorphic to the identity. -/ +@[expose] +def FiniteEtale.baseChangeSelfIso : baseChange R R ≅ 𝟭 (FiniteEtale R) := + NatIso.ofComponents (fun A ↦ isoMk (Algebra.TensorProduct.lid _ _)) <| fun {A B} f ↦ by + dsimp [baseChange] + ext + simp + +/-- The fiber functor for finite étale `R`-algebras at the geometric point `Ω`: This is the +functor sending `S` to `R`-algebra homomorphisms `S →ₐ[R] Ω`. -/ +@[expose, simps] +def FiniteEtale.fiber (R : Type u) [CommRing R] (Ω : Type w) [Field Ω] [Algebra R Ω] : + (FiniteEtale.{v} R)ᵒᵖ ⥤ FintypeCat.{max v w} where + obj S := .of (S.unop →ₐ[R] Ω) + map {S T} f := FintypeCat.homMk (·.comp f.unop.hom.hom) + +/-- If `k` is a field, this is the `Spec` functor sending a finite étale `k`-algebra `R` +to its finite prime spectrum. -/ +@[expose, simps] +def FiniteEtale.finiteSpec (k : Type u) [Field k] : (FiniteEtale.{v} k)ᵒᵖ ⥤ FintypeCat.{v} where + obj R := .of (PrimeSpectrum R.unop.obj) + map f := FintypeCat.homMk (PrimeSpectrum.comap f.unop.hom.hom) + +set_option backward.defeqAttrib.useBackward true in +/-- If the geometric point `Ω` factors through `S`, the fiber can be computed after base change +to `S`. -/ +@[expose] +def FiniteEtale.fiberIsoBaseChangeFiber : + FiniteEtale.fiber.{v} R Ω ≅ + (FiniteEtale.baseChange.{v} R S).op ⋙ FiniteEtale.fiber S Ω := + NatIso.ofComponents + (fun A ↦ FintypeCat.equivEquivIso (Algebra.TensorProduct.liftEquivRight _ _ _ _)) + +/-- If `Ω` is separably closed, the fiber functor for finite étale `Ω`-algebras +is naturally isomorphic to the (finite) `Spec` functor. -/ +@[expose] +noncomputable def FiniteEtale.fiberIsoFiniteSpec [IsSepClosed Ω] : + FiniteEtale.fiber Ω Ω ≅ FiniteEtale.finiteSpec Ω := + NatIso.ofComponents + fun R ↦ FintypeCat.equivEquivIso (Algebra.IsFiniteSplit.algHomEquivPrimeSpectrum _ _) + +/-- If `Ω` is separably closed, the fiber `S →ₐ[R] Ω` +is isomorphic to the prime spectrum of the base change `Ω ⊗[R] S`. -/ +@[expose] +noncomputable def FiniteEtale.fiberIsoComp [IsSepClosed Ω] : + FiniteEtale.fiber.{v} R Ω ≅ + (FiniteEtale.baseChange.{v} R Ω).op ⋙ FiniteEtale.finiteSpec.{max w v} Ω := + fiberIsoBaseChangeFiber _ _ Ω ≪≫ Functor.isoWhiskerLeft _ (fiberIsoFiniteSpec _) + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +/-- If `Ω` is a separably closed field, the category of finite étale `Ω`-algebras is +anti-equivalent to `FintypeCat`. -/ +@[expose, simps! functor inverse_obj inverse_map] +noncomputable def FiniteEtale.equivOfIsSepClosed (Ω : Type u) [Field Ω] [IsSepClosed Ω] : + (FiniteEtale.{u} Ω)ᵒᵖ ≌ FintypeCat.{u} := .symm + { functor.obj X := .op (.of _ (X → Ω)) + functor.map {X Y} f := .op (FiniteEtale.ofHom <| AlgHom.pi fun i ↦ Pi.evalAlgHom _ _ (f i)) + inverse := FiniteEtale.finiteSpec Ω + counitIso := + NatIso.ofComponents + (fun R ↦ (FiniteEtale.isoMk (Algebra.FormallyEtale.equivPiOfIsSepClosed Ω R.unop)).op) + fun {R S} f ↦ by + apply Quiver.Hom.unop_inj + ext x + exact funext fun p ↦ Algebra.FormallyEtale.equivPiOfIsSepClosed_comap _ _ _ + unitIso := NatIso.ofComponents + fun X ↦ FintypeCat.equivEquivIso <| + (Equiv.sigmaUnique _ _).symm.trans (PrimeSpectrum.sigmaToPiHomeo _).toEquiv + functor_unitIso_comp X := by + dsimp [FiniteEtale.finiteSpec] + apply Quiver.Hom.unop_inj + ext x i + dsimp + rw [FintypeCat.equivEquivIso_apply_hom, FintypeCat.homMk_apply] + dsimp + rw [← Pi.coe_evalAlgHom Ω] + simp [Algebra.FormallyEtale.equivPiOfIsSepClosed_comap, + Algebra.FormallyEtale.equivPiOfIsSepClosed_self_apply] } + +instance (Ω : Type u) [Field Ω] [IsSepClosed Ω] : (FiniteEtale.finiteSpec.{u} Ω).IsEquivalence := + (FiniteEtale.equivOfIsSepClosed.{u} Ω).isEquivalence_functor + +instance (Ω : Type u) [Field Ω] [IsSepClosed Ω] : (FiniteEtale.fiber.{u} Ω Ω).IsEquivalence := + Functor.isEquivalence_of_iso (FiniteEtale.fiberIsoFiniteSpec _).symm + +end CommAlgCat diff --git a/Mathlib/RingTheory/Etale/QuasiFinite.lean b/Mathlib/RingTheory/Etale/QuasiFinite.lean index 41233a5afde995..58240404b83dde 100644 --- a/Mathlib/RingTheory/Etale/QuasiFinite.lean +++ b/Mathlib/RingTheory/Etale/QuasiFinite.lean @@ -7,6 +7,7 @@ module public import Mathlib.RingTheory.Polynomial.UniversalFactorizationRing public import Mathlib.RingTheory.ZariskisMainTheorem +public import Mathlib.RingTheory.Ideal.Quotient.Over /-! # Etale local structure of finite maps @@ -393,8 +394,8 @@ lemma Algebra.exists_etale_isIdempotentElem_forall_liesOver_eq let Pf := P.map (algebraMap _ (Localization.Away f)) have : Pf.IsPrime := IsLocalization.isPrime_of_isPrime_disjoint (.powers f) _ _ ‹_› (by rwa [Ideal.disjoint_powers_iff_notMem_of_isPrime]) - have : Pf.LiesOver P := ⟨(IsLocalization.under_map_of_isPrime_disjoint (.powers f) _ ‹_› (by - rwa [Ideal.disjoint_powers_iff_notMem _ (Ideal.IsPrime.isRadical ‹_›)])).symm⟩ + have : Pf.LiesOver P := IsLocalization.liesOver_map_of_isPrime_disjoint (.powers f) _ (by + rwa [Ideal.disjoint_powers_iff_notMem _ (Ideal.IsPrime.isRadical ‹_›)]) let φ : R' ⊗[R] S →ₐ[R'] Localization.Away f ⊗[R] S := Algebra.TensorProduct.map (Algebra.ofId _ _) (.id _ _) let := φ.toAlgebra @@ -410,7 +411,7 @@ lemma Algebra.exists_etale_isIdempotentElem_forall_liesOver_eq change f ∉ P'.under _ rwa [← P'.over_def P] have : P'f.IsPrime := IsLocalization.isPrime_of_isPrime_disjoint _ _ _ ‹_› hP'f - have : P'f.LiesOver P' := ⟨(IsLocalization.under_map_of_isPrime_disjoint _ _ ‹_› hP'f).symm⟩ + have : P'f.LiesOver P' := IsLocalization.liesOver_map_of_isPrime_disjoint _ _ hP'f have : P'f.LiesOver P := .trans _ P' _ have : P'f.LiesOver Pf := ⟨congr($(PrimeSpectrum.localization_comap_injective (Localization.Away f) (.powers f) (a₁ := ⟨Pf, ‹_›⟩) @@ -435,3 +436,198 @@ lemma Algebra.exists_etale_isIdempotentElem_forall_liesOver_eq (Localization.Away f ⊗[R] S) (.powers (f ⊗ₜ 1)) (a₁ := ⟨P'', ‹_›⟩) (a₂ := ⟨P'f, ‹_›⟩) (PrimeSpectrum.ext ?_)).1) exact (H (P''.under _) inferInstance inferInstance hP'').trans (P'f.over_def P') + +open TensorProduct + +attribute [local instance] RingHom.ker_isPrime + +open scoped nonZeroDivisors + +attribute [local instance] Localization.AtPrime.algebraOfLiesOver + +/-- A key induction step of `exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq`. -/ +private theorem Algebra.exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq_aux + {R : Type u} {S : Type (max u v)} [CommRing R] [CommRing S] [Algebra R S] [Module.Finite R S] + (p : Ideal R) [p.IsPrime] (q : Ideal S) [q.IsPrime] + [q.LiesOver p] (R' : Type u) [CommRing R'] [Algebra R R'] [Algebra.Etale R R'] (P : Ideal R') + [P.IsPrime] [P.LiesOver p] (e : R' ⊗[R] S) (P' : Ideal (R' ⊗[R] S)) + [P'.IsPrime] [P'.LiesOver P] + (hP'q : Ideal.comap Algebra.TensorProduct.includeRight.toRingHom P' = q) + (heP' : e ∉ P') (hpP : Function.Bijective + (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p))) + (H : ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P → e ∉ P'' → P'' = P') + (R'' : Type u) [CommRing R''] [Algebra R' R''] [Algebra R R''] [IsScalarTower R R' R''] + [Algebra.Etale R' R''] (Q : Ideal R'') + [Q.IsPrime] [Q.LiesOver P] (n : ℕ) + (e' : Fin ((n + 1) + 1) → R'' ⊗[R] S) + (he' : CompleteOrthogonalIdempotents e') + (he'0 : e' 0 = Algebra.TensorProduct.map (Algebra.ofId R' R'') (AlgHom.id R S) e) + (Q' : Fin n → Ideal (R'' ⊗[R] S)) [∀ i, (Q' i).IsPrime] [∀ i, (Q' i).LiesOver Q] + (hPQ : Function.Bijective (Ideal.ResidueField.mapₐ P Q (Algebra.ofId _ _) (Q.over_def P))) + (hQ' : ∀ (i : Fin n), e' i.succ.castSucc ∉ Q' i) + (H' : ∀ (P'' : Ideal (R'' ⊗[R] S)), e' 0 ∈ P'' → P''.IsPrime → P''.LiesOver Q → + e' (.last _) ∈ P'' ∧ ∀ (i : Fin n), e' i.succ.castSucc ∉ P'' → P'' = Q' i) : + ∃ (R' : Type u) (_ : CommRing R') (_ : Algebra R R') (_ : Algebra.Etale R R') (P : Ideal R') + (_ : P.IsPrime) (_ : P.LiesOver p) (n : ℕ) (e : Fin (n + 1) → R' ⊗[R] S) + (_ : CompleteOrthogonalIdempotents e) (P' : Fin n → Ideal (R' ⊗[R] S)) + (_ : ∀ i, (P' i).IsPrime) (_ : ∀ i, (P' i).LiesOver P), + Function.Bijective (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p)) ∧ + (∀ i, e i.castSucc ∉ P' i) ∧ + ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P → + e (.last n) ∈ P'' ∧ ∀ i, e i.castSucc ∉ P'' → P'' = P' i := by + let φ := Algebra.TensorProduct.map (Algebra.ofId R' R'') (AlgHom.id R S) + have : Q.LiesOver p := .trans _ P _ + have hpQ : + Function.Bijective (Ideal.ResidueField.mapₐ p Q (Algebra.ofId _ _) (Q.over_def p)) := by + convert hPQ.comp hpP + rw [← @AlgHom.coe_restrictScalars' R R', ← AlgHom.coe_comp]; congr 1; ext + let P'φ := (Ideal.fiberIsoOfBijectiveResidueField hpQ).symm + (Ideal.fiberIsoOfBijectiveResidueField hpP ⟨P', ‹_›, ‹_›⟩) + have : P'φ.1.LiesOver P := .trans _ Q _ + have : (P'φ.1.comap φ.toRingHom).LiesOver P := inferInstanceAs ((P'φ.1.comap φ).LiesOver P) + have hP'φ : P'φ.1.comap φ.toRingHom = P' := by + apply Ideal.eq_of_comap_eq_comap_of_bijective_residueFieldMap hpP + rw [Ideal.comap_comap] + convert Ideal.comap_fiberIsoOfBijectiveResidueField_symm hpQ _ + · ext; simp [φ] + · simp; rfl + refine ⟨R'', inferInstance, _, .comp R R' R'', Q, ‹_›, .trans _ P _, _, _, he', Fin.cons P'φ + Q', Fin.cases P'φ.2.1 ?_, Fin.cases P'φ.2.2 ?_, hpQ, Fin.cases ?_ ?_, ?_⟩ + · intro P'' _ _ + by_cases heP'' : e ∈ P''.comap φ + · obtain ⟨h₁, h₂⟩ := H' P'' (by simpa [he'0]) inferInstance inferInstance + exact ⟨h₁, Fin.cases (fun h ↦ (h (by simpa [he'0])).elim) (by simpa)⟩ + · have : P''.LiesOver P := .trans _ Q _ + obtain rfl := H _ inferInstance inferInstance heP'' + have : ∀ i ≠ 0, e' i ∈ P'' := by + intro j hj + rw [← Ideal.IsPrime.mul_mem_left_iff (I := P'') heP''] + simp [φ, ← he'0, he'.ortho hj.symm] + refine ⟨by simp [this], Fin.cases (fun _ ↦ ?_) (by simp [this])⟩ + simp only [Fin.cons_zero] + apply Ideal.eq_of_comap_eq_comap_of_bijective_residueFieldMap hpQ + have : (φ.restrictScalars _).comp Algebra.TensorProduct.includeRight = + Algebra.TensorProduct.includeRight := by ext; simp [φ] + rw [← this] + exact congr(($hP'φ).comap Algebra.TensorProduct.includeRight).symm + · simp only [Fin.cons_succ]; infer_instance + · simp only [Fin.cons_succ]; infer_instance + · rw [← hP'φ] at heP'; simpa [he'0] + · simpa + +set_option backward.isDefEq.respectTransparency false in +/-- A less universe polymorphic version of +`exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq`. Use that instead. -/ +private lemma Algebra.exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq' + {R : Type u} {S : Type max u v} [CommRing R] [CommRing S] [Algebra R S] [Module.Finite R S] + (p : Ideal R) [p.IsPrime] : + ∃ (R' : Type u) (_ : CommRing R') (_ : Algebra R R') (_ : Algebra.Etale R R') (P : Ideal R') + (_ : P.IsPrime) (_ : P.LiesOver p) (n : ℕ) (e : Fin (n + 1) → R' ⊗[R] S) + (_ : CompleteOrthogonalIdempotents e) (P' : Fin n → Ideal (R' ⊗[R] S)) + (_ : ∀ i, (P' i).IsPrime) (_ : ∀ i, (P' i).LiesOver P), + Function.Bijective (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p)) ∧ + (∀ i, e i.castSucc ∉ P' i) ∧ + ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P → + e (.last n) ∈ P'' ∧ ∀ i, e i.castSucc ∉ P'' → P'' = P' i := by + induction h : (p.primesOver S).ncard using Nat.strong_induction_on generalizing R S with + | h n IH => + have : IsArtinianRing (p.ResidueField ⊗[R] S) := IsArtinianRing.of_finite p.ResidueField _ + have hpSfin : (p.primesOver S).Finite := + (PrimeSpectrum.primesOverOrderIsoFiber R S p).finite_iff.mpr inferInstance + cases n with + | zero => + have := (Set.ncard_eq_zero hpSfin).mp h + refine ⟨R, inferInstance, inferInstance, inferInstance, p, inferInstance, ⟨rfl⟩, 0, 1, + ⟨⟨by simp [IsIdempotentElem], + by simp only [Nat.reduceAdd, Pi.one_apply, mul_one, Subsingleton.pairwise]⟩, + by simp⟩, nofun, nofun, nofun, ?_, nofun, ?_⟩ + · rw! [ofId_self, Ideal.ResidueField.mapₐ_id]; exact Function.bijective_id + · exact fun P h₁ h₂ ↦ (this.le + ⟨show (P.comap Algebra.TensorProduct.includeRight.toRingHom).IsPrime from inferInstance, + ⟨by simp [P.over_def p, Ideal.under, Ideal.comap_comap]⟩⟩).elim + | succ n => + obtain ⟨q, hq, hq'⟩ := Set.nonempty_of_ncard_ne_zero (h.trans_ne (by simp)) + obtain ⟨R', _, _, _, P, _, _, e, he, P', _, _, hP'q, heP', hpP, _, H⟩ := + Algebra.exists_etale_isIdempotentElem_forall_liesOver_eq p q + have : (P.primesOver (R' ⊗[R] S ⧸ Ideal.span {e})).ncard < n + 1 := by + let F := Ideal.fiberIsoOfBijectiveResidueField hpP (S := S) + refine (Ideal.ncard_primesOver_quotient_singleton_lt_of_notMem _ _ + P' heP' (F.finite_iff.mpr hpSfin)).trans_le ?_ + rw [← h, ← Nat.card_coe_set_eq, ← Nat.card_coe_set_eq, Nat.card_congr F.toEquiv] + obtain ⟨R'', _, _, _, Q, _, _, n, e' : _ → R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}), + he', Q' : _ → Ideal (R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e})), _, _, hPQ, hQ', H'⟩ := + IH _ this (R := R') (S := R' ⊗[R] S ⧸ Ideal.span {e}) P rfl + let : Algebra R R'' := .compHom _ (algebraMap R R') + have : IsScalarTower R R' R'' := .of_algebraMap_eq' rfl + let φ := Algebra.TensorProduct.map (Algebra.ofId R' R'') (AlgHom.id R S) + let e₁ : R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}) ≃ₐ[R''] (R'' ⊗[R] S ⧸ Ideal.span {φ e}) := + tensorQuotientTensorEquiv (R'' := R'') e + obtain ⟨e'', he'', he''e'⟩ := CompleteOrthogonalIdempotents.exists_eq_comp_of_ker_eq_span + (Ideal.Quotient.mk (Ideal.span {φ e})) (I := Fin (n + 1)) (φ e) (he.map φ) (by simp) + (e₁ ∘ e') (he'.map e₁.toRingHom) (fun _ ↦ Ideal.Quotient.mk_surjective _) + have he''e'' (i : _) : e₁ (e' i) = e'' i := congr_fun he''e' i + have hψe'' (i : _) : (e' i) = e₁.symm (e'' i) := e₁.eq_symm_apply.mpr (he''e'' i) + refine exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq_aux p q R' P e P' + hP'q heP' hpP (fun P'' h₁ h₂ heP'' ↦ H P'' h₁ h₂ heP'') R'' Q n _ + ((CompleteOrthogonalIdempotents.equiv (finSuccEquiv _)).mpr he'') rfl + (Q' · |>.comap (e₁.symm.toAlgHom.comp (Ideal.Quotient.mkₐ _ _))) hPQ + (fun i ↦ by rw [Function.comp_def]; simpa [← hψe''] using hQ' i) ?_ + simp only [Function.comp_apply, finSuccEquiv_zero, finSuccEquiv_last, Fin.castSucc_succ, + finSuccEquiv_succ] + intro P'' heP'' _ _ + have : (P''.map (Ideal.Quotient.mk (.span {φ e}))).IsPrime := + Ideal.map_isPrime_of_surjective Ideal.Quotient.mk_surjective (by simpa [Ideal.span_le]) + have : (P''.map (Ideal.Quotient.mk (.span {φ e}))).LiesOver Q := ⟨by + have : P'' ⊔ Ideal.span {φ e} = P'' := by simpa [Ideal.span_le] + rw [← Ideal.under_under (B := R'' ⊗[R] S)] + simpa [Ideal.under, Ideal.comap_map_of_surjective _ Ideal.Quotient.mk_surjective, + ← RingHom.ker_eq_comap_bot, this] using P''.over_def Q⟩ + have := H' ((P''.map (Ideal.Quotient.mk (.span {φ e}))).comap e₁) inferInstance + (inferInstanceAs <| ((P''.map (Ideal.Quotient.mk (.span {φ e}))).comap + e₁.toAlgHom).LiesOver Q) + have hP'' : (1 - φ e) ∉ P'' := + fun h ↦ ‹P''.IsPrime›.one_notMem (by convert add_mem heP'' h; ring) + simp only [Ideal.mem_comap, he''e'', + Ideal.mem_map_span_singleton_iff_of_isIdempotentElem (he.map φ), + Ideal.IsPrime.mul_mem_left_iff hP''] at this + refine ⟨this.1, fun i hi ↦ (this.2 i hi).symm ▸ ?_⟩ + -- TODO: clean-up when `Ideal.comap` is refactored to take a `RingHom` + change _ = Ideal.comap (Ideal.Quotient.mk _) (Ideal.comap (e₁.symm.trans e₁).toRingHom _) + simp only [AlgEquiv.symm_trans_self, RingEquiv.toRingHom_eq_coe, + AlgEquiv.toRingEquiv_toRingHom, AlgEquiv.refl_toRingHom, Ideal.comap_id] + rw [Ideal.comap_map_of_surjective _ Ideal.Quotient.mk_surjective] + simpa [left_eq_sup, ← RingHom.ker_eq_comap_bot, Ideal.span_le] using heP'' + +/-- +If `S` is finite over `R`, and `p` is a prime of `R`, then there exists an étale neighborhood +`(R', P)` of `p` with `κ(p) = κ(P)` such that `R' ⊗[R] S ≃ₐ[R'] R₁ × ... × Rₙ × A`, +each `Rᵢ` has a unique prime `Pᵢ` lying over `P`, and no other prime in `R' ⊗[R] S` lies over `P`. + +This is merely an iterated application of `exists_etale_isIdempotentElem_forall_liesOver_eq`. +This is weaker than the corresponding statement of stacks project (in particular we asked for +`Module.Finite` instead of quasi finite when localized at `p`, so that we don't need to keep +track of this when passing to quotients and tensor products), and the only reason is that +the corresponding stronger statement is even harder to state and even more annoying to prove. +-/ +@[stacks 00UL] +lemma Algebra.exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq + {R : Type u} {S : Type v} [CommRing R] [CommRing S] [Algebra R S] [Module.Finite R S] + (p : Ideal R) [p.IsPrime] : + ∃ (R' : Type u) (_ : CommRing R') (_ : Algebra R R') (_ : Algebra.Etale R R') (P : Ideal R') + (_ : P.IsPrime) (_ : P.LiesOver p) (n : ℕ) (e : Fin (n + 1) → R' ⊗[R] S) + (_ : CompleteOrthogonalIdempotents e) (P' : Fin n → Ideal (R' ⊗[R] S)) + (_ : ∀ i, (P' i).IsPrime) (_ : ∀ i, (P' i).LiesOver P), + Function.Bijective (Ideal.ResidueField.mapₐ p P (Algebra.ofId _ _) (P.over_def p)) ∧ + (∀ i, e i.castSucc ∉ P' i) ∧ + ∀ (P'' : Ideal (R' ⊗[R] S)), P''.IsPrime → P''.LiesOver P → + e (.last n) ∈ P'' ∧ ∀ i, e i.castSucc ∉ P'' → P'' = P' i := by + have ⟨R', _, _, _, P, _, _, n, e, he, P', _, _, hP, hP', H⟩ := + exists_etale_completeOrthogonalIdempotents_forall_liesOver_eq' (S := ULift.{u} S) p + let e₁ : R' ⊗[R] S ≃ₐ[R'] R' ⊗[R] ULift.{u} S := + Algebra.TensorProduct.congr .refl ULift.algEquiv.symm + refine ⟨R', _, _, ‹_›, P, ‹_›, ‹_›, n, e₁.symm ∘ e, he.map _, + fun i ↦ (P' i).comap e₁.toAlgHom, inferInstance, inferInstance, hP, by simpa, + fun P'' _ _ ↦ ?_⟩ + have := H (P''.comap e₁.symm.toAlgHom) inferInstance inferInstance + refine ⟨by simpa using this.1, fun i hi ↦ ?_⟩ + simp [← this.2 i (by simpa), Ideal.comap_comapₐ] diff --git a/Mathlib/RingTheory/Extension/Presentation/Basic.lean b/Mathlib/RingTheory/Extension/Presentation/Basic.lean index 20b88f8700f9c0..075657d1a4168f 100644 --- a/Mathlib/RingTheory/Extension/Presentation/Basic.lean +++ b/Mathlib/RingTheory/Extension/Presentation/Basic.lean @@ -55,7 +55,6 @@ A presentation of an `R`-algebra `S` is a family of generators with `σ → MvPolynomial ι R`: The assignment of each relation to a polynomial in the generators. -/ -@[nolint checkUnivs] structure Algebra.Presentation extends Algebra.Generators R S ι where /-- The assignment of each relation to a polynomial in the generators. -/ relation : σ → toGenerators.Ring @@ -211,7 +210,7 @@ lemma _root_.Algebra.Generators.ker_localizationAway : (Ideal.Quotient.mkₐ R (Ideal.span {C r * X () - 1})) := by ext x simp only [aeval_X, Generators.localizationAway_val, AlgHom.coe_comp, - AlgEquiv.coe_algHom, Ideal.Quotient.mkₐ_eq_mk, Function.comp_apply] + AlgEquiv.coe_toAlgHom, Ideal.Quotient.mkₐ_eq_mk, Function.comp_apply] rw [IsLocalization.Away.mvPolynomialQuotientEquiv_apply, aeval_X] rw [Generators.ker_eq_ker_aeval_val, this, ← RingHom.ker_coe_toRingHom, AlgHom.comp_toRingHom, ← RingHom.comap_ker] diff --git a/Mathlib/RingTheory/Extension/Presentation/Core.lean b/Mathlib/RingTheory/Extension/Presentation/Core.lean index 9ed6f073826e2c..14d4919f4ff948 100644 --- a/Mathlib/RingTheory/Extension/Presentation/Core.lean +++ b/Mathlib/RingTheory/Extension/Presentation/Core.lean @@ -160,7 +160,7 @@ noncomputable def tensorModelOfHasCoeffsInv : S →ₐ[R] R ⊗[R₀] P.ModelOfH rw [← P.span_range_relation_eq_ker, Ideal.span_le] rintro a ⟨i, rfl⟩ simp only [SetLike.mem_coe, RingHom.mem_ker, AlgHom.coe_comp, - AlgEquiv.coe_algHom, Function.comp_apply, algebraTensorAlgEquiv_symm_relation] + AlgEquiv.coe_toAlgHom, Function.comp_apply, algebraTensorAlgEquiv_symm_relation] simp only [TensorProduct.map_tmul, AlgHom.coe_id, id_eq, Ideal.Quotient.mkₐ_eq_mk, Ideal.Quotient.mk_span_range, tmul_zero]).comp (P.quotientEquiv.restrictScalars R).symm.toAlgHom diff --git a/Mathlib/RingTheory/Extension/Presentation/Submersive.lean b/Mathlib/RingTheory/Extension/Presentation/Submersive.lean index 08b03267550fd6..956fcb7d611e89 100644 --- a/Mathlib/RingTheory/Extension/Presentation/Submersive.lean +++ b/Mathlib/RingTheory/Extension/Presentation/Submersive.lean @@ -64,7 +64,6 @@ with relations equipped with an injective `map : relations → vars`. This map determines how the differential of `P` is constructed. See `PreSubmersivePresentation.differential` for details. -/ -@[nolint checkUnivs] structure PreSubmersivePresentation extends Algebra.Presentation R S ι σ where /-- A map from the relations type to the variables type. Used to compute the differential. -/ map : σ → ι @@ -495,7 +494,6 @@ variable [Finite σ] A `PreSubmersivePresentation` is submersive if its Jacobian is a unit in `S` and the presentation is finite. -/ -@[nolint checkUnivs] structure SubmersivePresentation extends PreSubmersivePresentation.{t, w} R S ι σ where jacobian_isUnit : IsUnit toPreSubmersivePresentation.jacobian diff --git a/Mathlib/RingTheory/FiniteType.lean b/Mathlib/RingTheory/FiniteType.lean index 3748e2ff54e2fb..68ec9e65447b48 100644 --- a/Mathlib/RingTheory/FiniteType.lean +++ b/Mathlib/RingTheory/FiniteType.lean @@ -611,7 +611,8 @@ end MonoidAlgebra section Orzech open Submodule Module Module.Finite in -/-- Any commutative ring `R` satisfies the `OrzechProperty`, that is, for any finitely generated +/-- +Any commutative ring `R` satisfies the `OrzechProperty`, that is, for any finitely generated `R`-module `M`, any surjective homomorphism `f : N →ₗ[R] M` from a submodule `N` of `M` to `M` is injective. @@ -630,7 +631,12 @@ then it's easy to see that `i` and `f` restrict to `N' →ₗ[A] M'`, and the restricted version of `f` is surjective, hence by Noetherian case, it is also injective, in particular, if `f n = 0`, then `n = 0`. -See also Orzech's original paper: *Onto endomorphisms are isomorphisms* [orzech1971]. -/ +See also Orzech's original paper: *Onto endomorphisms are isomorphisms* [orzech1971]. + +This implies that nontrivial commutative rings satisfy the strong rank condition: +see `strongRankCondition_of_orzechProperty` in `Mathlib.LinearAlgebra.InvariantBasisNumber`. +A shortcut instance `commRing_strongRankCondition` is also provided. +-/ instance (priority := 100) CommRing.orzechProperty (R : Type*) [CommRing R] : OrzechProperty R := by refine ⟨fun {M} _ _ _ {N} f hf ↦ ?_⟩ diff --git a/Mathlib/RingTheory/Finiteness/Basic.lean b/Mathlib/RingTheory/Finiteness/Basic.lean index 4ea58f2b32969a..7cb4b5fed17525 100644 --- a/Mathlib/RingTheory/Finiteness/Basic.lean +++ b/Mathlib/RingTheory/Finiteness/Basic.lean @@ -324,6 +324,7 @@ universe u in instance shrink [Module.Finite R M] [Small.{u} M] : Module.Finite R (Shrink.{u} M) := Module.Finite.equiv (Shrink.linearEquiv R M).symm +set_option linter.dupNamespace false in @[deprecated (since := "2026-04-18")] alias Module.finite_shrink := shrink /-- A submodule is finite as a module iff it is finitely generated. -/ diff --git a/Mathlib/RingTheory/Finiteness/Cofinite.lean b/Mathlib/RingTheory/Finiteness/Cofinite.lean index 7e23470116e2b4..98924e5a4a6fcd 100644 --- a/Mathlib/RingTheory/Finiteness/Cofinite.lean +++ b/Mathlib/RingTheory/Finiteness/Cofinite.lean @@ -49,10 +49,20 @@ theorem _root_.Module.Finite.iff_cofg_bot : (⊥ : Submodule R M).CoFG ↔ Modul theorem CoFG.fg_of_isCompl {S T : Submodule R M} (hST : IsCompl S T) (hS : S.CoFG) : T.FG := Module.Finite.iff_fg.mp <| Module.Finite.equiv <| quotientEquivOfIsCompl S T hST +/-- Over a noetherian ring, if `S` and `T` are disjoint and `T` is CoFG, then `S` is FG. -/ +theorem CoFG.fg_of_disjoint [IsNoetherianRing R] {S T : Submodule R M} (hST : Disjoint S T) + (hT : T.CoFG) : S.FG := + .of_disjoint_of_isNoetherian_quotient hST + +/-- If `S` and `T` are co-disjoint and `S` is FG, then `T` is CoFG. -/ +theorem FG.cofg_of_codisjoint {S T : Submodule R M} (hST : Codisjoint S T) (hS : S.FG) : + T.CoFG := + have := Module.Finite.iff_fg.mpr hS + .of_surjective (T.mkQ.domRestrict S) (by simp [← LinearMap.range_eq_top, hST.symm.eq_top]) + /-- A complement of an FG submodule is CoFG. -/ -theorem FG.cofg_of_isCompl {S T : Submodule R M} (hST : IsCompl S T) (hS : S.FG) : T.CoFG := by - haveI := Module.Finite.iff_fg.mpr hS - exact Module.Finite.equiv (quotientEquivOfIsCompl T S hST.symm).symm +theorem FG.cofg_of_isCompl {S T : Submodule R M} (hST : IsCompl S T) (hS : S.FG) : T.CoFG := + hS.cofg_of_codisjoint hST.codisjoint /-- A submodule that contains a CoFG submodule is CoFG. -/ theorem CoFG.of_le {S T : Submodule R M} (hT : S ≤ T) (hS : S.CoFG) : T.CoFG := by diff --git a/Mathlib/RingTheory/FormalGroup/Basic.lean b/Mathlib/RingTheory/FormalGroup/Basic.lean index 27e991b35e0781..4c6d3bf0156cb7 100644 --- a/Mathlib/RingTheory/FormalGroup/Basic.lean +++ b/Mathlib/RingTheory/FormalGroup/Basic.lean @@ -12,8 +12,8 @@ public import Mathlib.Tactic.Ring.NamePowerVars Let `R` be a commutative ring, a one dimensional formal group law is a formal power series `F(X,Y) ∈ R⟦X,Y⟧` such that -· `F(X,Y) = X + Y + higher order terms`. -· `F(F(X,Y),Z) = F(X,F(Y,Z))`. + * `F(X,Y) = X + Y + higher order terms`. + * `F(F(X,Y),Z) = F(X,F(Y,Z))`. Under this definition, we can prove that `F(X,0) = X` and `F(0,X) = X`. Moreover, there is a unique power series `i(X)` such that `F(X, i(X)) = 0`, which is considered to be the inverse @@ -21,14 +21,17 @@ of the formal group law `F(X,Y)`. ## Main definitions/lemmas -* Definition of one dimensional formal group law. +* `FormalGroup R`: definition of one dimensional formal group law over commutative ring `R`. -* Properties: `F(X,0) = 0` and `F(0,X) = X`. +* Properties: `F(X,0) = X` and `F(0,X) = X`. -* Additive formal group laws and multiplicative formal group laws. +* Additive formal group laws `𝔾ₐ` and multiplicative formal group laws `𝔾ₘ`. -* Instance: Group instance defined by the formal group law `F` over the ideal - `PowerSeries.hasEvalIdeal`. +* `F.Point σ` taking values in the formal power series ring `MvPowerSeries σ R` with the property +that constant coefficient is nilpotent. We have the following typeclass: +- `AddMonoid (F.Point σ)` +when `F` is a commutative formal group law +- `AddCommMonoid (F.Point σ)` ## References * [Hazewinkel, Michiel. Formal Groups and Applications][hazewinkel1978] @@ -37,7 +40,7 @@ of the formal group law `F(X,Y)`. @[expose] public section -variable {R : Type*} [CommRing R] {S : Type*} [CommRing S] [Algebra R S] {σ τ : Type*} +variable {R : Type*} [CommRing R] {S : Type*} [CommRing S] {σ τ : Type*} noncomputable section @@ -71,24 +74,71 @@ instance FormalGroup.coeToPowerSeries : Coe (FormalGroup R) (MvPowerSeries (Fin class FormalGroup.IsComm (F : FormalGroup R) : Prop where comm : F = (F : MvPowerSeries (Fin 2) R).subst ![X₁, X₀] +lemma FormalGroup.assoc' (F : FormalGroup R) {f₀ f₁ f₂ : MvPowerSeries σ R} + (h₀ : PowerSeries.HasSubst f₀) (h₁ : PowerSeries.HasSubst f₁) (h₂ : PowerSeries.HasSubst f₂) : + F.toPowerSeries.subst ![F.toPowerSeries.subst ![f₀, f₁], f₂] = + F.toPowerSeries.subst ![f₀, F.toPowerSeries.subst ![f₁, f₂]] := by + obtain aux₁ := HasSubst.cons_subst_zero_left (0 : Fin 3) 1 2 F.zero_constantCoeff + obtain aux₂ := HasSubst.cons_subst_zero_right (0 : Fin 3) 1 2 F.zero_constantCoeff + have : HasSubst ![f₀, f₁, f₂] := + hasSubst_of_constantCoeff_nilpotent fun s => by fin_cases s <;> simpa + calc + _ = (F.toPowerSeries.subst ![F.toPowerSeries.subst ![Y₀, Y₁], Y₂]).subst ![f₀, f₁, f₂] := by + rw [subst_comp_subst_apply aux₁ this] + congr! 2 with s + fin_cases s + · simp only [Nat.succ_eq_add_one, Nat.reduceAdd, Fin.zero_eta, Fin.isValue, + Matrix.cons_val_zero, subst_comp_subst_apply HasSubst.X_X this] + congr! 2 with s + fin_cases s <;> simp [subst_X this] + · simp [subst_X this] + _ = _ := by + rw [F.assoc, subst_comp_subst_apply aux₂ this] + congr! 2 with s + fin_cases s + · simp [subst_X this] + · simp only [Fin.mk_one, Matrix.cons_val_one, Matrix.cons_val_fin_one, + subst_comp_subst_apply HasSubst.X_X this] + congr! 2 with s + fin_cases s <;> simp [subst] + +lemma FormalGroup.comm' (F : FormalGroup R) [F.IsComm] {f g : MvPowerSeries σ R} + (hf : PowerSeries.HasSubst f) (hg : PowerSeries.HasSubst g) : + F.toPowerSeries.subst ![f, g] = F.toPowerSeries.subst ![g, f] := by + nth_rw 1 [IsComm.comm] + rw [subst_comp_subst_apply HasSubst.X_X <| hasSubst_of_constantCoeff_nilpotent (by simp [hf, hg])] + congr! 2 with s + fin_cases s <;> simp [subst] + namespace FormalGroup -variable {σ : Type} (F : FormalGroup R) +variable {σ : Type*} (F : FormalGroup R) set_option linter.unusedVariables false in -/-- `Point F σ` represents the mathematical space of points of a formal group $F$ -taking values in the formal power series ring `R⟦X_σ⟧`. +/-- `F.Point σ` represents the mathematical space of points of a formal group $F$ +taking values in the formal power series ring `MvPowerSeries σ R` with the property +that constant coefficient is nilpotent. -Mathematically, a 1-dimensional formal group law $F$ over a ring $R$ defines a group +TODO: Mathematically, a 1-dimensional formal group law $F$ over a ring $R$ defines a group structure on the elements of a complete local $R$-algebra (specifically, its maximal ideal) via the substitution operation $x +_F y = F(x, y)$. -/ @[nolint unusedArguments] -def Point (F : FormalGroup R) (σ : Type) := MvPowerSeries σ R +def Point (F : FormalGroup R) (σ : Type*) := {f : MvPowerSeries σ R // PowerSeries.HasSubst f} instance : Add (F.Point σ) where - add x y := (F : MvPowerSeries (Fin 2) R).subst ![x, y] + add x y := ⟨F.toPowerSeries.subst ![x.val, y.val], + IsNilpotent_subst (by simp [hasSubst_of_constantCoeff_nilpotent, x.prop, y.prop]) + (F.zero_constantCoeff ▸ IsNilpotent.zero)⟩ + +@[simp] +lemma add_apply {x y : F.Point σ} : (x + y).val = F.toPowerSeries.subst ![x.val, y.val] := by + rfl + +instance : Zero (F.Point σ) where + zero := ⟨0, PowerSeries.HasSubst.zero⟩ -/- TODO : Zero, SMul, Inv instance. -/ +@[simp] +lemma zero_apply : (0 : F.Point σ).val = (0 : MvPowerSeries σ R) := rfl /-- Additive formal group law `𝔾ₐ(X,Y) = X + Y`. -/ @[simps] @@ -127,7 +177,6 @@ def 𝔾ₘ : FormalGroup R where instance : (𝔾ₘ (R := R)).IsComm where comm := by simp [subst_add .X_X, subst_mul .X_X, subst_X .X_X, add_comm, mul_comm] -omit [Algebra R S] in /-- Given an algebra map `f : R →+* S` and a formal group law `F` over `R`, then `f_* F` is a formal group law formal group law over `S`. This is constructed by applying `f` to all coefficients of the underlying power series. -/ @@ -145,3 +194,149 @@ def map (f : R →+* S) : FormalGroup S where ← map_subst (HasSubst.cons_subst_zero_right (0 : Fin 3) 1 2 F.zero_constantCoeff)] end FormalGroup + +section + +namespace FormalGroup + +variable (F : FormalGroup R) + +/-- An abbreviation of $F(X,0)$ for a formal group $F$. -/ +abbrev Xzero : PowerSeries R := subst ![PowerSeries.X, 0] F.toPowerSeries + +lemma constantCoeff_Xzero : F.Xzero.constantCoeff = 0 := by + simp [PowerSeries.constantCoeff, Xzero, PowerSeries.X, MvPowerSeries.constantCoeff_subst_eq_zero + HasSubst.X_zero _ F.zero_constantCoeff] + +@[simp] +lemma coeff_one_Xzero : F.Xzero.coeff 1 = 1 := by + rw [PowerSeries.coeff, coeff_subst, finsum_eq_single _ (single 0 1)] + · simp [F.lin_coeff_X] + · intro d hd + by_cases hd₁ : d 1 = 0 + · by_cases hd₀ : d 0 = 0 + · simp [hd₀, hd₁] + simp [hd₁, PowerSeries.coeff_X_pow] + grind + simp [hd₁] + · exact HasSubst.X_zero + +@[simp] +lemma Xzero_subst_Xzero : F.Xzero.subst F.Xzero = F.Xzero := by + calc + _ = F.toPowerSeries.subst ![F.toPowerSeries.subst ![PowerSeries.X, 0], 0] := by + have : PowerSeries.HasSubst (subst ![PowerSeries.X (R := R), 0] F.toPowerSeries) := by + refine PowerSeries.HasSubst.of_constantCoeff_zero' ?_ + rw [PowerSeries.constantCoeff, PowerSeries.X, constantCoeff_subst_eq_zero HasSubst.X_zero + (by simp) F.zero_constantCoeff] + rw [PowerSeries.subst, subst_comp_subst_apply _ this.const] + · congr! 2 with d + fin_cases d + · simp [← PowerSeries.subst_def, PowerSeries.subst_X this] + · simp [← PowerSeries.subst_def, ← PowerSeries.coe_substAlgHom this] + · exact HasSubst.X_zero + _ = _ := by + have : ![0, 0] = (0 : Fin 2 → PowerSeries R) := by + ext x : 1; fin_cases x <;> rfl + simp [F.assoc', this, subst_zero_of_constantCoeff_zero F.zero_constantCoeff, + PowerSeries.HasSubst.X', PowerSeries.HasSubst] + +lemma Xzero_eq_X : F.Xzero = PowerSeries.X := by + haveI : Invertible (F.Xzero.coeff 1) := (coeff_one_Xzero F) ▸ invertibleOne + calc + _ = F.Xzero.substInv.subst (F.Xzero.subst F.Xzero) := by + have aux₀ : PowerSeries.HasSubst F.Xzero := + PowerSeries.HasSubst.of_constantCoeff_zero' <| constantCoeff_Xzero F + rw [← PowerSeries.subst_comp_subst_apply aux₀ aux₀, PowerSeries.subst_substInv_left _ + F.constantCoeff_Xzero , PowerSeries.subst_X aux₀, Xzero] + _ = _ := by + rw [Xzero_subst_Xzero, F.Xzero.subst_substInv_left F.constantCoeff_Xzero] + +/-- An abbreviation of $F(0,X)$ for a formal group $F$. -/ +abbrev zeroX : PowerSeries R := subst ![0, PowerSeries.X] F.toPowerSeries + +lemma constantCoeff_zeroX : F.zeroX.constantCoeff = 0 := by + simp [PowerSeries.constantCoeff, zeroX, PowerSeries.X, MvPowerSeries.constantCoeff_subst_eq_zero + HasSubst.zero_X _ F.zero_constantCoeff] + +@[simp] +lemma coeff_one_zeroX : F.zeroX.coeff 1 = 1 := by + rw [PowerSeries.coeff, coeff_subst, finsum_eq_single _ (single 1 1)] + · simp [F.lin_coeff_Y] + · intro d hd + by_cases hd₁ : d 0 = 0 + · by_cases hd₀ : d 1 = 0 + · simp [hd₀, hd₁] + simp [hd₁, PowerSeries.coeff_X_pow] + grind + simp [hd₁] + · exact HasSubst.zero_X + +@[simp] +lemma zeroX_subst_zeroX : F.zeroX.subst F.zeroX = F.zeroX := by + calc + _ = F.toPowerSeries.subst ![0, F.toPowerSeries.subst ![0, PowerSeries.X]] := by + have : PowerSeries.HasSubst (subst ![0, PowerSeries.X (R := R)] F.toPowerSeries) := by + refine PowerSeries.HasSubst.of_constantCoeff_zero' ?_ + rw [PowerSeries.constantCoeff, PowerSeries.X, constantCoeff_subst_eq_zero HasSubst.zero_X + (by simp) F.zero_constantCoeff] + rw [PowerSeries.subst, subst_comp_subst_apply _ this.const] + · congr! 2 with d + fin_cases d + · simp [← PowerSeries.subst_def, ← PowerSeries.coe_substAlgHom this] + · simp [← PowerSeries.subst_def, PowerSeries.subst_X this] + · exact HasSubst.zero_X + _ = _ := by + have : ![0, 0] = (0 : Fin 2 → PowerSeries R) := by ext x : 1; fin_cases x <;> rfl + simp [← F.assoc', this, subst_zero_of_constantCoeff_zero F.zero_constantCoeff, + PowerSeries.HasSubst.X', PowerSeries.HasSubst] + +lemma zeroX_eq_X : F.zeroX = PowerSeries.X := by + haveI : Invertible (F.zeroX.coeff 1) := (coeff_one_zeroX F) ▸ invertibleOne + calc + _ = F.zeroX.substInv.subst (F.zeroX.subst F.zeroX) := by + have aux₀ : PowerSeries.HasSubst F.zeroX := + PowerSeries.HasSubst.of_constantCoeff_zero' <| F.constantCoeff_zeroX + rw [← PowerSeries.subst_comp_subst_apply aux₀ aux₀, PowerSeries.subst_substInv_left _ + F.constantCoeff_zeroX, PowerSeries.subst_X aux₀, zeroX] + _ = _ := by + rw [zeroX_subst_zeroX, F.zeroX.subst_substInv_left F.constantCoeff_zeroX] + +theorem add_zero {f : MvPowerSeries σ R} (hf : PowerSeries.HasSubst f) : + F.toPowerSeries.subst ![f, 0] = f := by + calc + _ = PowerSeries.subst f (F.toPowerSeries.subst ![PowerSeries.X (R := R), 0]) := by + rw [PowerSeries.subst, subst_comp_subst_apply _ hf.const] + · congr! 2 with s + fin_cases s + · simp [PowerSeries.X, subst] + · simp [subst, eval₂] + exact HasSubst.X_zero + _ = _ := by + simp [Xzero_eq_X, PowerSeries.subst_X hf] + +theorem zero_add {f : MvPowerSeries σ R} (hf : PowerSeries.HasSubst f) : + F.toPowerSeries.subst ![0, f] = f := by + calc + _ = PowerSeries.subst f (F.toPowerSeries.subst ![0, PowerSeries.X (R := R)]) := by + rw [PowerSeries.subst, subst_comp_subst_apply _ hf.const] + · congr! 2 with s + fin_cases s + · simp [subst, eval₂] + · simp [PowerSeries.X, subst] + · exact HasSubst.zero_X + _ = _ := by + simp [zeroX_eq_X, PowerSeries.subst_X hf] + +instance : AddMonoid (F.Point σ) where + zero_add x := Subtype.ext (zero_add F x.prop) + add_zero x := Subtype.ext (add_zero F x.prop) + nsmul := nsmulRec + add_assoc x y z := Subtype.ext <| F.assoc' x.prop y.prop z.prop + +instance [F.IsComm] : AddCommMonoid (F.Point σ) where + add_comm x y := Subtype.ext <| F.comm' x.prop y.prop + +end FormalGroup + +end diff --git a/Mathlib/RingTheory/FractionalIdeal/Basic.lean b/Mathlib/RingTheory/FractionalIdeal/Basic.lean index 6b00bb74fdf3a6..25256416bf34ef 100644 --- a/Mathlib/RingTheory/FractionalIdeal/Basic.lean +++ b/Mathlib/RingTheory/FractionalIdeal/Basic.lean @@ -184,7 +184,7 @@ theorem ext {I J : FractionalIdeal S P} : (∀ x, x ∈ I ↔ x ∈ J) → I = J SetLike.ext @[simp] - theorem equivNum_apply [IsDomain R] [Module.IsTorsionFree R P] [Nontrivial P] +theorem equivNum_apply [IsDomain R] [Module.IsTorsionFree R P] [Nontrivial P] {I : FractionalIdeal S P} (h_nz : (I.den : R) ≠ 0) (x : I) : algebraMap R P (equivNum h_nz x) = I.den • x := by change Algebra.linearMap R P _ = _ diff --git a/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean b/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean index baacd12d69fcf3..6b6c5faa288492 100644 --- a/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean +++ b/Mathlib/RingTheory/GradedAlgebra/AlgHom.lean @@ -98,9 +98,11 @@ initialize_simps_projections GradedAlgHom (toFun → apply) theorem coe_mks {f : A → B} (h₁ h₂ h₃ h₄ h₅ h₆) : ⇑(⟨⟨⟨⟨⟨f, h₁⟩, h₂⟩, h₃, h₄⟩, h₅⟩, h₆⟩ : 𝒜 →ₐᵍ[R] ℬ) = f := rfl -theorem coe_algHom_mk {f : A →ₐ[R] B} (h) : ((⟨f, h⟩ : 𝒜 →ₐᵍ[R] ℬ) : A →ₐ[R] B) = f := by +theorem coe_toAlgHom_mk {f : A →ₐ[R] B} (h) : ((⟨f, h⟩ : 𝒜 →ₐᵍ[R] ℬ) : A →ₐ[R] B) = f := by dsimp only +@[deprecated (since := "2026-05-05")] alias coe_algHom_mk := coe_toAlgHom_mk + variable (f : 𝒜 →ₐᵍ[R] ℬ) theorem coe_fn_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → (A → B)) := @@ -109,23 +111,25 @@ theorem coe_fn_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) theorem coe_fn_inj {f₁ f₂ : 𝒜 →ₐᵍ[R] ℬ} : (f₁ : A → B) = f₂ ↔ f₁ = f₂ := DFunLike.coe_fn_eq -theorem coe_algHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →ₐ[R] B) := +theorem coe_toAlgHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →ₐ[R] B) := fun _ _ h ↦ coe_fn_injective congr($h) +@[deprecated (since := "2026-05-05")] alias coe_algHom_injective := coe_toAlgHom_injective + theorem toGradedRingHom_injective : Function.Injective (toGradedRingHom (𝒜 := 𝒜) (ℬ := ℬ)) := fun _ _ h ↦ coe_fn_injective congr($h) theorem coe_linearMap_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →ₗ[R] B) := - AlgHom.toLinearMap_injective.comp coe_algHom_injective + AlgHom.toLinearMap_injective.comp coe_toAlgHom_injective theorem coe_ringHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →+* B) := - AlgHom.coe_ringHom_injective.comp coe_algHom_injective + AlgHom.coe_ringHom_injective.comp coe_toAlgHom_injective theorem coe_monoidHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →* B) := - AlgHom.coe_monoidHom_injective.comp coe_algHom_injective + AlgHom.coe_monoidHom_injective.comp coe_toAlgHom_injective theorem coe_addMonoidHom_injective : Function.Injective ((↑) : (𝒜 →ₐᵍ[R] ℬ) → A →+ B) := - AlgHom.coe_addMonoidHom_injective.comp coe_algHom_injective + AlgHom.coe_addMonoidHom_injective.comp coe_toAlgHom_injective /-- Consider using `congr($H x)` instead. -/ protected theorem congr_fun {f₁ f₂ : 𝒜 →ₐᵍ[R] ℬ} (H : f₁ = f₂) (x : A) : f₁ x = f₂ x := @@ -215,11 +219,11 @@ instance : Monoid (𝒜 →ₐᵍ[R] 𝒜) where lemma cancel_right {g₁ g₂ : ℬ →ₐᵍ[R] 𝒞} {f : 𝒜 →ₐᵍ[R] ℬ} (hf : Function.Surjective f) : g₁.comp f = g₂.comp f ↔ g₁ = g₂ := - ⟨fun h ↦ coe_algHom_injective <| (AlgHom.cancel_right hf).1 congr($h), fun h ↦ h ▸ rfl⟩ + ⟨fun h ↦ coe_toAlgHom_injective <| (AlgHom.cancel_right hf).1 congr($h), fun h ↦ h ▸ rfl⟩ lemma cancel_left {g₁ g₂ : 𝒜 →ₐᵍ[R] ℬ} {f : ℬ →ₐᵍ[R] 𝒞} (hf : Function.Injective f) : f.comp g₁ = f.comp g₂ ↔ g₁ = g₂ := - ⟨fun h ↦ coe_algHom_injective <| (AlgHom.cancel_left hf).1 congr($h), fun h ↦ h ▸ rfl⟩ + ⟨fun h ↦ coe_toAlgHom_injective <| (AlgHom.cancel_left hf).1 congr($h), fun h ↦ h ▸ rfl⟩ /-- We enrich the existing function `toAlgHom` with the structure of a `MonoidHom`, to produce a bundled function that we now call `toEnd`. -/ @@ -256,9 +260,12 @@ variable (R₀ : Type*) [CommSemiring R₀] [Algebra R₀ R] @[simp] lemma coe_restrictScalars : ⇑(f.restrictScalars R₀) = f := rfl -@[simp] lemma restrictScalars_coe_algHom : +@[simp] lemma restrictScalars_toAlgHom : (f : A →ₐ[R] B).restrictScalars R₀ = f.restrictScalars R₀ := rfl +@[deprecated (since := "2026-05-05")] +alias restrictScalars_coe_algHom := restrictScalars_toAlgHom + @[simp] lemma restrictScalars_coe_linearMap : (f : A →ₗ[R] B).restrictScalars R₀ = f.restrictScalars R₀ := rfl diff --git a/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean b/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean index 46e82e06a69fe6..5a6b85e0106a28 100644 --- a/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean +++ b/Mathlib/RingTheory/GradedAlgebra/TensorProduct.lean @@ -128,8 +128,8 @@ def liftEquiv : (𝒜 →ₐᵍ[R] (ℬ · |>.restrictScalars R)) ≃ ((𝒜 · invFun f := { AlgHom.liftEquiv R S A B |>.symm f with map_mem hx := f.map_mem <| tmul_mem_baseChange_of_mem _ hx } - left_inv f := coe_algHom_injective <| by simp - right_inv f := coe_algHom_injective <| by simp + left_inv f := coe_toAlgHom_injective <| by simp + right_inv f := coe_toAlgHom_injective <| by simp variable {𝒜 ℬ} diff --git a/Mathlib/RingTheory/HahnSeries/Addition.lean b/Mathlib/RingTheory/HahnSeries/Addition.lean index 3980046d3b6e78..7bd4bb28ae90f8 100644 --- a/Mathlib/RingTheory/HahnSeries/Addition.lean +++ b/Mathlib/RingTheory/HahnSeries/Addition.lean @@ -16,6 +16,7 @@ public import Mathlib.Tactic.FastInstance /-! # Additive properties of Hahn series + If `Γ` is ordered and `R` has zero, then `R⟦Γ⟧` consists of formal series over `Γ` with coefficients in `R`, whose supports are partially well-ordered. With further structure on `R` and `Γ`, we can add further structure on `R⟦Γ⟧`. When `R` has an addition operation, `R⟦Γ⟧` also has addition by adding diff --git a/Mathlib/RingTheory/HahnSeries/Binomial.lean b/Mathlib/RingTheory/HahnSeries/Binomial.lean index 46ec61006315cc..dbdb40cbe67f5d 100644 --- a/Mathlib/RingTheory/HahnSeries/Binomial.lean +++ b/Mathlib/RingTheory/HahnSeries/Binomial.lean @@ -10,6 +10,7 @@ public import Mathlib.RingTheory.PowerSeries.Binomial /-! # Binomial expansions of powers of Hahn Series + We introduce binomial expansions using `embDomain`. ## Main Definitions diff --git a/Mathlib/RingTheory/HahnSeries/HEval.lean b/Mathlib/RingTheory/HahnSeries/HEval.lean index c5d26e685b07bb..1fd494bca65b66 100644 --- a/Mathlib/RingTheory/HahnSeries/HEval.lean +++ b/Mathlib/RingTheory/HahnSeries/HEval.lean @@ -94,7 +94,7 @@ theorem support_powerSeriesFamily_subset {x : V⟦Γ⟧} (a b : PowerSeries R) ( ((powers x) n).coeff g ≠ 0 := by refine exists_ne_zero_of_sum_ne_zero ?_ simpa [PowerSeries.coeff_mul, sum_smul, mul_smul, h] using hn - simp only [powers_of_orderTop_pos h, mem_antidiagonal] at he + simp only [powers_of_orderTop_pos h, HasAntidiagonal.mem_antidiagonal] at he obtain ⟨c, hcn, hc⟩ := he simp only [coe_image, Set.Finite.coe_toFinset, Set.mem_image] use c @@ -129,7 +129,7 @@ theorem hsum_powerSeriesFamily_mul {x : V⟦Γ⟧} (a b : PowerSeries R) : (fun _ _ => by simp [smul_smul, mul_comm, pow_add])).symm · intro ij hij simp only [coe_sigma, coe_image, Set.mem_sigma_iff, Set.mem_image, Prod.exists, mem_coe, - mem_antidiagonal, and_true] + HasAntidiagonal.mem_antidiagonal, and_true] use ij.1, ij.2 simp_all · intro i hi his @@ -140,7 +140,7 @@ theorem hsum_powerSeriesFamily_mul {x : V⟦Γ⟧} (a b : PowerSeries R) : simp only [powers_of_orderTop_pos h, Set.Finite.coe_toFinset, Set.mem_image, Function.mem_support, ne_eq, Prod.exists, not_exists, not_and] at his exact his m n - simp only [mem_sigma, mem_antidiagonal] at hi + simp only [mem_sigma, HasAntidiagonal.mem_antidiagonal] at hi rw [mul_comm ((PowerSeries.coeff i.snd.1) a), ← hi.2, mul_smul, pow_add] exact hisc i.snd.1 i.snd.2 <| Sigma.eq hi.2 (by simp) · simp only [h, not_false_eq_true, powerSeriesFamily_of_not_orderTop_pos, diff --git a/Mathlib/RingTheory/HahnSeries/Multiplication.lean b/Mathlib/RingTheory/HahnSeries/Multiplication.lean index b79bc4fe3c4980..fe2d366daab26a 100644 --- a/Mathlib/RingTheory/HahnSeries/Multiplication.lean +++ b/Mathlib/RingTheory/HahnSeries/Multiplication.lean @@ -15,6 +15,7 @@ public import Mathlib.RingTheory.HahnSeries.Addition /-! # Multiplicative properties of Hahn series + If `Γ` is ordered and `R` has zero, then `R⟦Γ⟧` consists of formal series over `Γ` with coefficients in `R`, whose supports are partially well-ordered. This module introduces multiplication and scalar multiplication on Hahn series. If `Γ` is an ordered cancellative @@ -400,7 +401,7 @@ theorem of_symm_smul_of_eq_mul [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} : theorem coeff_mul [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} {a : Γ} : (x * y).coeff a = - ∑ ij ∈ addAntidiagonal x.isPWO_support y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd := + ∑ ij ∈ antidiagonal x.isPWO_support y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd := rfl protected lemma map_mul [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring S] (f : R →ₙ+* S) @@ -408,9 +409,9 @@ protected lemma map_mul [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring ext simp only [map_coeff, coeff_mul, map_sum, map_mul] refine Eq.symm (sum_subset (fun gh hgh => ?_) (fun gh hgh hz => ?_)) - · simp_all only [mem_addAntidiagonal, mem_support, map_coeff, ne_eq, and_true] + · simp_all only [mem_antidiagonal, mem_support, map_coeff, ne_eq, and_true] exact ⟨fun h => hgh.1 (map_zero f ▸ congrArg f h), fun h => hgh.2.1 (map_zero f ▸ congrArg f h)⟩ - · simp_all only [mem_addAntidiagonal, mem_support, ne_eq, map_coeff, and_true, + · simp_all only [mem_antidiagonal, mem_support, ne_eq, map_coeff, and_true, not_and, not_not] by_cases h : f (x.coeff gh.1) = 0 · exact mul_eq_zero_of_left h (f (y.coeff gh.2)) @@ -419,13 +420,13 @@ protected lemma map_mul [NonUnitalNonAssocSemiring R] [NonUnitalNonAssocSemiring theorem coeff_mul_left' [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} {a : Γ} {s : Set Γ} (hs : s.IsPWO) (hxs : x.support ⊆ s) : (x * y).coeff a = - ∑ ij ∈ addAntidiagonal hs y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd := + ∑ ij ∈ antidiagonal hs y.isPWO_support a, x.coeff ij.fst * y.coeff ij.snd := HahnModule.coeff_smul_left hs hxs theorem coeff_mul_right' [NonUnitalNonAssocSemiring R] {x y : R⟦Γ⟧} {a : Γ} {s : Set Γ} (hs : s.IsPWO) (hys : y.support ⊆ s) : (x * y).coeff a = - ∑ ij ∈ addAntidiagonal x.isPWO_support hs a, x.coeff ij.fst * y.coeff ij.snd := + ∑ ij ∈ antidiagonal x.isPWO_support hs a, x.coeff ij.fst * y.coeff ij.snd := HahnModule.coeff_smul_right hs hys instance [NonUnitalNonAssocSemiring R] : Distrib R⟦Γ⟧ where @@ -453,14 +454,14 @@ theorem coeff_mul_single_add [NonUnitalNonAssocSemiring R] {r : R} {x : R⟦Γ rw [sum_congr _ fun _ _ => rfl, sum_empty] ext ⟨a1, a2⟩ simp only [notMem_empty, not_and, Set.mem_singleton_iff, - mem_addAntidiagonal, iff_false] + mem_antidiagonal, iff_false] rintro h2 rfl h1 rw [← add_right_cancel h1] at hx exact h2 hx trans ∑ ij ∈ {(a, b)}, x.coeff ij.fst * (single b r).coeff ij.snd · apply sum_congr _ fun _ _ => rfl ext ⟨a1, a2⟩ - simp only [Set.mem_singleton_iff, Prod.mk_inj, mem_addAntidiagonal, mem_singleton] + simp only [Set.mem_singleton_iff, Prod.mk_inj, mem_antidiagonal, mem_singleton] constructor · rintro ⟨_, rfl, h1⟩ exact ⟨add_right_cancel h1, rfl⟩ @@ -621,7 +622,7 @@ instance [NonUnitalCommSemiring R] : NonUnitalCommSemiring R⟦Γ⟧ where mul_comm x y := by ext simp_rw [coeff_mul, mul_comm] - exact Finset.sum_equiv (Equiv.prodComm _ _) (fun _ ↦ swap_mem_addAntidiagonal.symm) <| by simp + exact Finset.sum_equiv (Equiv.prodComm _ _) (fun _ ↦ swap_mem_antidiagonal.symm) <| by simp instance [CommSemiring R] : CommSemiring R⟦Γ⟧ where instance [NonUnitalNonAssocRing R] : NonUnitalNonAssocRing R⟦Γ⟧ where @@ -780,7 +781,7 @@ theorem single_mul_single {a b : Γ} {r s : R} : · rw [h, coeff_mul_single_add] simp · rw [coeff_single_of_ne h, coeff_mul, sum_eq_zero] - simp_rw [mem_addAntidiagonal] + simp_rw [mem_antidiagonal] rintro ⟨y, z⟩ ⟨hy, hz, rfl⟩ rw [eq_of_mem_support_single hy, eq_of_mem_support_single hz] at h exact (h rfl).elim @@ -863,13 +864,13 @@ theorem embDomain_mul [NonUnitalNonAssocSemiring R] (f : Γ ↪o Γ') simp only [coeff_mul, embDomain_coeff] trans ∑ ij ∈ - (addAntidiagonal x.isPWO_support y.isPWO_support g).map + (antidiagonal x.isPWO_support y.isPWO_support g).map (f.toEmbedding.prodMap f.toEmbedding), (embDomain f x).coeff ij.1 * (embDomain f y).coeff ij.2 · simp apply sum_subset · rintro ⟨i, j⟩ hij - simp only [mem_map, mem_addAntidiagonal, + simp only [mem_map, mem_antidiagonal, Function.Embedding.coe_prodMap, mem_support, Prod.exists] at hij obtain ⟨i, j, ⟨hx, hy, rfl⟩, rfl, rfl⟩ := hij simp [hx, hy, hf] @@ -877,9 +878,9 @@ theorem embDomain_mul [NonUnitalNonAssocSemiring R] (f : Γ ↪o Γ') contrapose! h2 obtain ⟨i, _, rfl⟩ := support_embDomain_subset (ne_zero_and_ne_zero_of_mul h2).1 obtain ⟨j, _, rfl⟩ := support_embDomain_subset (ne_zero_and_ne_zero_of_mul h2).2 - simp only [mem_map, mem_addAntidiagonal, + simp only [mem_map, mem_antidiagonal, Function.Embedding.coe_prodMap, mem_support, Prod.exists] - simp only [mem_addAntidiagonal, embDomain_coeff, mem_support, ← hf, + simp only [mem_antidiagonal, embDomain_coeff, mem_support, ← hf, OrderEmbedding.eq_iff_eq] at h1 exact ⟨i, j, h1, rfl⟩ · rw [embDomain_notin_range hg, eq_comm] @@ -975,11 +976,11 @@ instance [IsCancelAdd R] [IsCancelMulZero R] : IsCancelMulZero R⟦Γ⟧ where have ha : y.coeff a ≠ z.coeff a := this.min_mem hyz refine ⟨x.order + a, ?_⟩ rwa [coeff_mul, coeff_mul, sum_subset subset_union_left, - sum_subset (s₁ := addAntidiagonal _ _ _) subset_union_right, + sum_subset (s₁ := antidiagonal _ _ _) subset_union_right, sum_eq_sum_iff_single (i := (x.order, a)), mul_right_inj' (coeff_order_eq_zero.not.2 hx)] · simp [hx] grind - · simp +contextual only [mem_union, mem_addAntidiagonal, mul_eq_mul_left_iff, Prod.mk.injEq, + · simp +contextual only [mem_union, mem_antidiagonal, mul_eq_mul_left_iff, Prod.mk.injEq, ne_eq, ← and_or_left, ← or_and_right, or_false, and_imp, Prod.forall, mem_support, not_and] rintro b c hxb - hbc hbc' contrapose! hbc' @@ -997,11 +998,11 @@ instance [IsCancelAdd R] [IsCancelMulZero R] : IsCancelMulZero R⟦Γ⟧ where have ha : y.coeff a ≠ z.coeff a := this.min_mem hyz refine ⟨a + x.order, ?_⟩ rwa [coeff_mul, coeff_mul, sum_subset subset_union_left, - sum_subset (s₁ := addAntidiagonal _ _ _) subset_union_right, + sum_subset (s₁ := antidiagonal _ _ _) subset_union_right, sum_eq_sum_iff_single (i := (a, x.order)), mul_left_inj' (coeff_order_eq_zero.not.2 hx)] · simp [hx] grind - · simp +contextual only [mem_union, mem_addAntidiagonal, mul_eq_mul_right_iff, Prod.mk.injEq, + · simp +contextual only [mem_union, mem_antidiagonal, mul_eq_mul_right_iff, Prod.mk.injEq, ne_eq, ← or_and_right, or_false, and_imp, Prod.forall, mem_support, not_and] rintro b c - hxb hbc hbc' contrapose! hbc' diff --git a/Mathlib/RingTheory/HahnSeries/PowerSeries.lean b/Mathlib/RingTheory/HahnSeries/PowerSeries.lean index 0dec034a06063a..a5bb85af445c40 100644 --- a/Mathlib/RingTheory/HahnSeries/PowerSeries.lean +++ b/Mathlib/RingTheory/HahnSeries/PowerSeries.lean @@ -12,6 +12,7 @@ public import Mathlib.Data.Finsupp.PWO /-! # Comparison between Hahn series and power series + If `Γ` is ordered and `R` has zero, then `R⟦Γ⟧` consists of formal series over `Γ` with coefficients in `R`, whose supports are partially well-ordered. With further structure on `R` and `Γ`, we can add further structure on `R⟦Γ⟧`. When `R` is a semiring and `Γ = ℕ`, then @@ -70,8 +71,8 @@ def toPowerSeries : R⟦ℕ⟧ ≃+* PowerSeries R where refine (sum_filter_ne_zero _).symm.trans <| (sum_congr ?_ fun _ _ ↦ rfl).trans <| sum_filter_ne_zero _ ext m - simp only [mem_antidiagonal, mem_addAntidiagonal, and_congr_left_iff, mem_filter, - mem_support] + simp only [HasAntidiagonal.mem_antidiagonal, Finset.mem_antidiagonal, and_congr_left_iff, + mem_filter, mem_support] rintro h rw [and_iff_right (left_ne_zero_of_mul h), and_iff_right (right_ne_zero_of_mul h)] @@ -169,8 +170,8 @@ def toMvPowerSeries {σ : Type*} [Finite σ] : R⟦σ →₀ ℕ⟧ ≃+* MvPowe refine (sum_filter_ne_zero _).symm.trans <| (sum_congr ?_ fun _ _ ↦ rfl).trans <| sum_filter_ne_zero _ ext m - simp only [and_congr_left_iff, mem_addAntidiagonal, mem_filter, mem_support, - Finset.mem_antidiagonal] + simp only [and_congr_left_iff, Finset.mem_antidiagonal, mem_filter, mem_support, + HasAntidiagonal.mem_antidiagonal] rintro h rw [and_iff_right (left_ne_zero_of_mul h), and_iff_right (right_ne_zero_of_mul h)] diff --git a/Mathlib/RingTheory/HahnSeries/Summable.lean b/Mathlib/RingTheory/HahnSeries/Summable.lean index 022cf4cf2e1e47..1816db6f432fe3 100644 --- a/Mathlib/RingTheory/HahnSeries/Summable.lean +++ b/Mathlib/RingTheory/HahnSeries/Summable.lean @@ -11,6 +11,7 @@ public import Mathlib.Data.Rat.Cast.Lemmas /-! # Summable families of Hahn Series + We introduce a notion of formal summability for families of Hahn series, and define a formal sum function. This theory is applied to characterize invertible Hahn series whose coefficients are in a commutative domain. @@ -555,7 +556,7 @@ theorem mul_eq_smul (s : SummableFamily Γ R α) (t : SummableFamily Γ R β) : rfl theorem coeff_hsum_mul (s : SummableFamily Γ R α) (t : SummableFamily Γ R β) (g : Γ) : - (mul s t).hsum.coeff g = ∑ gh ∈ addAntidiagonal s.isPWO_iUnion_support + (mul s t).hsum.coeff g = ∑ gh ∈ antidiagonal s.isPWO_iUnion_support t.isPWO_iUnion_support g, (s.hsum.coeff gh.1) * (t.hsum.coeff gh.2) := by simp_rw [← smul_eq_mul, mul_eq_smul] exact coeff_smul s t g @@ -694,17 +695,17 @@ theorem pow_finite_co_support {x : R⟦Γ⟧} (hx : 0 < x.orderTop) (g : Γ) : swap; · exact Set.finite_empty.subset fun n hn => hg (Set.mem_iUnion.2 ⟨n, hn⟩) apply hpwo.isWF.induction hg intro y ys hy - refine ((((addAntidiagonal x.isPWO_support hpwo y).finite_toSet.biUnion - fun ij hij => hy ij.snd (mem_addAntidiagonal.1 (mem_coe.1 hij)).2.1 ?_).image Nat.succ).union + refine ((((antidiagonal x.isPWO_support hpwo y).finite_toSet.biUnion + fun ij hij => hy ij.snd (mem_antidiagonal.1 (mem_coe.1 hij)).2.1 ?_).image Nat.succ).union (Set.finite_singleton 0)).subset ?_ - · obtain ⟨hi, _, rfl⟩ := mem_addAntidiagonal.1 (mem_coe.1 hij) + · obtain ⟨hi, _, rfl⟩ := mem_antidiagonal.1 (mem_coe.1 hij) exact lt_add_of_pos_left ij.2 <| lt_of_lt_of_le ((zero_lt_orderTop_iff h0).mp hx) <| order_le_of_coeff_ne_zero <| Function.mem_support.mp hi · rintro (_ | n) hn · exact Set.mem_union_right _ (Set.mem_singleton 0) · obtain ⟨i, hi, j, hj, rfl⟩ := support_mul_subset hn refine Set.mem_union_left _ ⟨n, Set.mem_iUnion.2 ⟨⟨j, i⟩, Set.mem_iUnion.2 ⟨?_, hi⟩⟩, rfl⟩ - simp only [mem_coe, mem_addAntidiagonal, mem_support, ne_eq, Set.mem_iUnion] + simp only [mem_coe, mem_antidiagonal, mem_support, ne_eq, Set.mem_iUnion] exact ⟨hj, ⟨n, hi⟩, add_comm j i⟩ /-- A summable family of powers of a Hahn series `x`. If `x` has non-positive `orderTop`, then diff --git a/Mathlib/RingTheory/Ideal/Defs.lean b/Mathlib/RingTheory/Ideal/Defs.lean index f8a8c64474e4b2..aee2dc55cc58e9 100644 --- a/Mathlib/RingTheory/Ideal/Defs.lean +++ b/Mathlib/RingTheory/Ideal/Defs.lean @@ -146,11 +146,18 @@ theorem mul_sub_mul_mem [I.IsTwoSided] rw [show a * c - b * d = (a - b) * c + b * (c - d) by rw [sub_mul, mul_sub]; abel] exact I.add_mem (I.mul_mem_right _ h1) (I.mul_mem_left _ h2) -/-- -The subgroup of elements `g` of `G` such that `∀ x, g • x - x ∈ I`. --/ -abbrev inertia (G : Type*) [Group G] [MulAction G α] (I : Ideal α) : - Subgroup G := AddSubgroup.inertia I.toAddSubgroup G +section inertia + +variable (G : Type*) [Group G] [MulAction G α] (I : Ideal α) + +/-- The subgroup of elements `g` of `G` such that `∀ x, g • x - x ∈ I`. -/ +abbrev inertia : Subgroup G := I.toAddSubgroup.inertia G + +variable {I G} in +theorem coe_mem_inertia {H : Subgroup G} {σ : H} : ↑σ ∈ I.inertia G ↔ σ ∈ I.inertia H := + I.toAddSubgroup.coe_mem_inertia + +end inertia end Ideal diff --git a/Mathlib/RingTheory/Ideal/Height.lean b/Mathlib/RingTheory/Ideal/Height.lean index 457a89a3fc05a2..6fc30fe2b79b4e 100644 --- a/Mathlib/RingTheory/Ideal/Height.lean +++ b/Mathlib/RingTheory/Ideal/Height.lean @@ -6,7 +6,6 @@ Authors: Wanyi He, Jiedong Jiang, Jingting Wang, Andrew Yang, Shouxin Zhang module public import Mathlib.Algebra.Module.SpanRank -public import Mathlib.RingTheory.Ideal.MinimalPrime.Localization public import Mathlib.RingTheory.Ideal.MinimalPrime.Noetherian public import Mathlib.RingTheory.Spectrum.Prime.Topology @@ -50,6 +49,12 @@ lemma Ideal.height_eq_inf_minimalPrimes : I.height = ⨅ J ∈ I.minimalPrimes, have := hp.isPrime exact (Ideal.height_eq_primeHeight _).symm +lemma Ideal.exists_isPrime_height_eq {I : Ideal R} {n : ℕ} (hI : I.height = n) : + ∃ (p : Ideal R) (_ : p.IsPrime) (_ : I ≤ p), p.height = n := by + simp only [Ideal.height, ENat.iInf_eq_coe_iff] at hI + rcases hI with ⟨⟨p, ⟨⟨⟨hpp, hIp⟩, _⟩, h⟩, -⟩, -⟩ + exact ⟨p, hpp, hIp, h ▸ p.height_eq_primeHeight⟩ + /-- An ideal has finite height if it is either the unit ideal or its height is finite. We include the unit ideal in order to have the instance `IsNoetherianRing R → FiniteHeight I`. -/ @[mk_iff] @@ -247,6 +252,17 @@ lemma Ideal.height_bot [Nontrivial R] : (⊥ : Ideal R).height = 0 := by simp only [ENat.iInf_eq_zero] refine ⟨p, hp, haveI := hp.isPrime; height_eq_zero_iff.mpr hp⟩ +@[simp] +lemma Ideal.height_eq_zero_iff_eq_bot [IsDomain R] {I : Ideal R} : I.height = 0 ↔ I = ⊥ := by + refine ⟨fun hI ↦ ?_, fun hI0 ↦ by simp [hI0]⟩ + rcases exists_isPrime_height_eq hI with ⟨p, _, hIp, hp0⟩ + rw [CharP.cast_eq_zero, height_eq_zero_iff, IsDomain.minimalPrimes_eq_singleton_bot, + Set.mem_singleton_iff] at hp0 + exact bot_unique (hIp.trans_eq hp0) + +theorem Ideal.ne_bot_of_height_eq_one [IsDomain R] {I : Ideal R} (h : I.height = 1) : I ≠ ⊥ := + I.height_eq_zero_iff_eq_bot.not.mp (ne_zero_of_eq_one h) + /-- In a trivial commutative ring, the height of any ideal is `∞`. -/ @[simp, nontriviality] lemma Ideal.height_of_subsingleton [Subsingleton R] : I.height = ⊤ := by @@ -542,3 +558,13 @@ lemma Ring.krullDimLE_of_isLocalization_maximal {n : ℕ} exact h P end isLocalization + +lemma Ideal.eq_span_singleton_of_height_eq_one [IsDomain R] {p : Ideal R} [p.IsPrime] + (h1 : p.height = 1) {x : R} (hx : x ∈ p) (hxp : Prime x) : p = span {x} := by + have : (span {x}).IsPrime := by simp [span_singleton_prime hxp.ne_zero, hxp] + have : p.FiniteHeight := by simp [p.finiteHeight_iff, h1] + by_contra! hne + apply hxp.ne_zero + rw [← span_singleton_eq_bot, ← height_eq_zero_iff_eq_bot, ← Order.lt_one_iff, ← h1] + refine height_strict_mono_of_isPrime_of_isPrime (lt_of_le_of_ne ?_ hne.symm) + simp only [p.span_singleton_le_iff_mem, hx] diff --git a/Mathlib/RingTheory/Ideal/Maps.lean b/Mathlib/RingTheory/Ideal/Maps.lean index 523c5a0fdc457d..62de906cb2c249 100644 --- a/Mathlib/RingTheory/Ideal/Maps.lean +++ b/Mathlib/RingTheory/Ideal/Maps.lean @@ -6,6 +6,7 @@ Authors: Kenny Lau module public import Mathlib.Data.DFinsupp.Module +public import Mathlib.Order.KrullDimension public import Mathlib.RingTheory.Ideal.Operations /-! @@ -392,6 +393,7 @@ theorem comap_le_comap_iff_of_surjective (hf : Function.Surjective f) (I J : Ide le_comap_of_map_le ((map_comap_of_surjective f hf I).le.trans h)⟩ /-- The map on ideals induced by a surjective map preserves inclusion. -/ +@[simps] def orderEmbeddingOfSurjective (hf : Function.Surjective f) : Ideal S ↪o Ideal R where toFun := comap f inj' _ _ eq := SetLike.ext' (Set.preimage_injective.mpr hf <| SetLike.ext'_iff.mp eq) @@ -588,6 +590,15 @@ theorem comap_map_of_surjective (hf : Function.Surjective f) (I : Ideal R) : add_sub_cancel s r⟩) (sup_le (map_le_iff_le_comap.1 le_rfl) (comap_mono bot_le)) +theorem coheight_comap_of_surjective (hf : Function.Surjective f) (I : Ideal S) : + Order.coheight (I.comap f) = Order.coheight I := by + let φ := orderEmbeddingOfSurjective f hf + refine (Order.coheight_eq_of_strictMono φ φ.strictMono (fun J K h ↦ ⟨K.map f, ?_, ?_⟩) I).symm + · rw [← J.map_comap_of_surjective f hf] + apply lt_of_le_not_ge (map_mono h.le) + simpa [map_le_iff_le_comap, φ] using h.not_ge + · exact (K.comap_map_of_surjective f hf).trans (sup_of_le_left ((comap_mono bot_le).trans h.le)) + /-- Correspondence theorem -/ def relIsoOfSurjective (hf : Function.Surjective f) : Ideal S ≃o { p : Ideal R // comap f ⊥ ≤ p } where diff --git a/Mathlib/RingTheory/Ideal/Maximal.lean b/Mathlib/RingTheory/Ideal/Maximal.lean index c6cae02a1be57a..f4cfefe052d189 100644 --- a/Mathlib/RingTheory/Ideal/Maximal.lean +++ b/Mathlib/RingTheory/Ideal/Maximal.lean @@ -148,6 +148,9 @@ variable [CommSemiring α] (I : Ideal α) theorem span_singleton_prime {p : α} (hp : p ≠ 0) : IsPrime (span ({p} : Set α)) ↔ Prime p := by simp [isPrime_iff, Prime, span_singleton_eq_top, hp, mem_span_singleton] +theorem isPrime_span_singleton_of_prime {p : α} (hp : Prime p) : (span {p}).IsPrime := by + simp [Ideal.span_singleton_prime hp.ne_zero, hp] + theorem IsMaximal.isPrime {I : Ideal α} (H : I.IsMaximal) : I.IsPrime := ⟨H.1.1, @fun x y hxy => or_iff_not_imp_left.2 fun hx => by diff --git a/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean b/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean index 86337c0e1acc2c..e55fb5aae24973 100644 --- a/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean +++ b/Mathlib/RingTheory/Ideal/Norm/RelNorm.lean @@ -398,27 +398,26 @@ See `Ideal.relNorm_eq_pow_of_isMaximal` for a statement that does not require th be Galois. -/ theorem relNorm_eq_pow_of_isPrime_isGalois [p.IsMaximal] [P.IsPrime] - [IsGalois (FractionRing R) (FractionRing S)] : relNorm R P = p ^ p.inertiaDeg P := by + [IsGalois (FractionRing R) (FractionRing S)] : relNorm R P = p ^ P.inertiaDeg' R := by have : P.IsMaximal := IsMaximal.of_liesOver_isMaximal P p let G := Gal(FractionRing S/FractionRing R) let := IsIntegralClosure.MulSemiringAction R (FractionRing R) (FractionRing S) S have := IsGaloisGroup.of_isFractionRing G R S (FractionRing R) (FractionRing S) by_cases hp : p = ⊥ - · have h : p.inertiaDeg P ≠ 0 := Nat.ne_zero_iff_zero_lt.mpr <| inertiaDeg_pos p P + · have h : P.inertiaDeg' R ≠ 0 := (inertiaDeg'_pos P R).ne' have hP : P = ⊥ := by rw [hp] at hPp exact eq_bot_of_liesOver_bot R P rw [hp, hP, relNorm_bot, bot_pow] - rwa [hp, hP] at h + rwa [hP] at h obtain ⟨s, hs⟩ := exists_relNorm_eq_pow_of_isPrime P p - suffices s = p.inertiaDeg P by rwa [this] at hs + suffices s = P.inertiaDeg' R by rwa [this] at hs have h₀ : ∀ Q ∈ (p.primesOver S).toFinset, - relNorm R Q ^ ramificationIdx p Q = p ^ ((p.ramificationIdxIn S) * s) := by + relNorm R Q ^ Q.ramificationIdx R = p ^ ((p.ramificationIdxIn S) * s) := by intro Q hQ rw [Set.mem_toFinset] at hQ have : Q.IsPrime := hQ.1 have : Q.LiesOver p := hQ.2 - rw [ramificationIdx_eq_ramificationIdx' p Q hp] rw [← ramificationIdxIn_eq_ramificationIdx p Q G] obtain ⟨σ, rfl⟩ := Ideal.exists_smul_eq_of_isGaloisGroup p P Q G rw [relNorm_smul, hs, ← pow_mul, mul_comm] @@ -430,13 +429,12 @@ theorem relNorm_eq_pow_of_isPrime_isGalois [p.IsMaximal] [P.IsPrime] ← Set.ncard_eq_toFinset_card', ((IsLeftCancelMulZero.mul_left_cancel_of_ne_zero hp).pow_injective _).eq_iff, mul_right_inj' (IsDedekindDomain.primesOver_ncard_ne_zero p S), - mul_right_inj' (ramificationIdxIn_ne_zero G), - inertiaDegIn_eq_inertiaDeg p P G, ← inertiaDeg_eq_inertiaDeg' p P] at h + mul_right_inj' (ramificationIdxIn_ne_zero G), inertiaDegIn_eq_inertiaDeg p P G] at h rw [one_eq_top] exact IsMaximal.ne_top inferInstance theorem relNorm_eq_pow_of_isMaximal [PerfectField (FractionRing R)] [P.IsMaximal] [p.IsMaximal] : - relNorm R P = p ^ p.inertiaDeg P := by + relNorm R P = p ^ P.inertiaDeg' R := by let T := Ring.NormalClosure R S obtain ⟨Q, hQ₁, hQ₂⟩ : ∃ Q : Ideal T, Q.IsMaximal ∧ Q.LiesOver P := exists_maximal_ideal_liesOver_of_isIntegral P @@ -445,8 +443,7 @@ theorem relNorm_eq_pow_of_isMaximal [PerfectField (FractionRing R)] [P.IsMaximal have : IsGalois (FractionRing S) (FractionRing T) := IsGalois.tower_top_of_isGalois (FractionRing R) (FractionRing S) (FractionRing T) rwa [← relNorm_relNorm R S, relNorm_eq_pow_of_isPrime_isGalois Q P, map_pow, - inertiaDeg_algebra_tower p P Q, pow_mul, pow_left_inj] at h - exact Nat.ne_zero_iff_zero_lt.mpr <| inertiaDeg_pos P Q + inertiaDeg'_tower (R := R) P Q, pow_mul, pow_left_inj (inertiaDeg'_pos Q S).ne'] at h end relNorm_prime @@ -471,13 +468,9 @@ theorem absNorm_relNorm [PerfectField (FractionRing R)] (I : Ideal S) : have : Q.IsMaximal := Ring.DimensionLEOne.maximalOfPrime hQ' hQ.1 let P := under R Q let p := absNorm (under ℤ P) - have : NeZero P := ⟨under_ne_bot R hQ'⟩ - have : Q.LiesOver P := by simp [liesOver_iff, P] have : Q.LiesOver (span {(p : ℤ)}) := LiesOver.trans Q P _ - have : Fact (p.Prime) := ⟨Nat.absNorm_under_prime _⟩ - have hp : Prime (p : ℤ) := Nat.prime_iff_prime_int.mp <| Nat.absNorm_under_prime _ - rw [relNorm_eq_pow_of_isMaximal Q P, map_pow, absNorm_eq_pow_inertiaDeg Q hp, - absNorm_eq_pow_inertiaDeg P hp, inertiaDeg_algebra_tower (span {(p : ℤ)}) P Q, pow_mul] + rw [relNorm_eq_pow_of_isMaximal Q P, map_pow, ← pow_inertiaDeg' p, ← pow_inertiaDeg' p, + ← pow_mul, ← inertiaDeg'_tower] theorem relNorm_int (I : Ideal S) : relNorm ℤ I = Ideal.span {(absNorm I : ℤ)} := by diff --git a/Mathlib/RingTheory/Ideal/Operations.lean b/Mathlib/RingTheory/Ideal/Operations.lean index e0824b1e53b0ca..7017583d9e6b72 100644 --- a/Mathlib/RingTheory/Ideal/Operations.lean +++ b/Mathlib/RingTheory/Ideal/Operations.lean @@ -397,6 +397,9 @@ end IsTwoSided theorem mul_eq_bot [NoZeroDivisors R] : I * J = ⊥ ↔ I = ⊥ ∨ J = ⊥ := Submodule.mul_eq_bot +theorem pow_eq_bot [IsReduced R] {n : ℕ} (hn : n ≠ 0) : I ^ n = ⊥ ↔ I = ⊥ := + Submodule.pow_eq_bot hn + instance {S A : Type*} [Semiring S] [SMul R S] [AddCommMonoid A] [Module R A] [Module S A] [IsScalarTower R S A] [IsTorsionFree R A] {I : Submodule S A} : IsTorsionFree R I := (I.restrictScalars R).instIsTorsionFree @@ -435,7 +438,7 @@ theorem span_singleton_mul_left_inj [IsDomain R] [I.IsTwoSided] [J.IsTwoSided] theorem mul_le_inf [I.IsTwoSided] : I * J ≤ I ⊓ J := mul_le.2 fun r hri s hsj => ⟨I.mul_mem_right s hri, J.mul_mem_left r hsj⟩ -lemma inf_ne_bot_of_ne_bot [NoZeroDivisors R] {I J : Ideal R} [I.IsTwoSided] [J.IsTwoSided] +lemma inf_ne_bot_of_ne_bot [NoZeroDivisors R] {I J : Ideal R} [I.IsTwoSided] (hI : I ≠ ⊥) (hJ : J ≠ ⊥) : I ⊓ J ≠ ⊥ := by grw [← bot_lt_iff_ne_bot, ← mul_le_inf, bot_lt_iff_ne_bot, Ne, mul_eq_bot] @@ -886,12 +889,11 @@ variable {I J} in theorem IsRadical.inf (hI : IsRadical I) (hJ : IsRadical J) : IsRadical (I ⊓ J) := by rw [IsRadical, radical_inf]; exact inf_le_inf hI hJ -lemma isRadical_bot_iff : - (⊥ : Ideal R).IsRadical ↔ IsReduced R := by +lemma isRadical_bot_iff : (⊥ : Ideal R).IsRadical ↔ IsReduced R := by simp only [IsRadical, SetLike.le_def, Ideal.mem_radical_iff, Ideal.mem_bot, forall_exists_index, isReduced_iff, IsNilpotent] -lemma isRadical_bot [IsReduced R] : (⊥ : Ideal R).IsRadical := by rwa [Ideal.isRadical_bot_iff] +lemma isRadical_bot [IsReduced R] : (⊥ : Ideal R).IsRadical := by rwa [isRadical_bot_iff] /-- `Ideal.radical` as an `InfTopHom`, bundling in that it distributes over `inf`. -/ def radicalInfTopHom : InfTopHom (Ideal R) (Ideal R) where diff --git a/Mathlib/RingTheory/Ideal/Over.lean b/Mathlib/RingTheory/Ideal/Over.lean index 0965eb9bcd53a5..cc305ab486e261 100644 --- a/Mathlib/RingTheory/Ideal/Over.lean +++ b/Mathlib/RingTheory/Ideal/Over.lean @@ -90,7 +90,7 @@ section Semiring variable (A : Type*) [CommSemiring A] {B C : Type*} [Semiring B] [Semiring C] [Algebra A B] [Algebra A C] (P : Ideal B) {Q : Ideal C} (p : Ideal A) - {G : Type*} [Group G] [MulSemiringAction G B] [SMulCommClass G A B] (g : G) + {G : Type*} [Group G] [MulSemiringAction G B] (g : G) /-- The ideal obtained by pulling back the ideal `P` from `B` to `A`. -/ abbrev under : Ideal A := Ideal.comap (algebraMap A B) P @@ -103,10 +103,19 @@ instance IsPrime.under [hP : P.IsPrime] : (P.under A).IsPrime := hP.comap (algebraMap A B) @[simp] -lemma under_smul : (g • P : Ideal B).under A = P.under A := by +lemma under_smul [SMulCommClass G A B] : (g • P : Ideal B).under A = P.under A := by ext a rw [mem_comap, mem_comap, mem_pointwise_smul_iff_inv_smul_mem, smul_algebraMap] +@[simp] +theorem smul_under [MulSemiringAction G A] [SMulDistribClass G A B] : + g • P.under A = (g • P).under A := by + conv_lhs => rw [pointwise_smul_eq_comap, ← comap_coe, under_def, comap_comap] + conv_rhs => rw [pointwise_smul_eq_comap, ← comap_coe, under_def, comap_comap] + congr + ext + simp [algebraMap.smul'] + variable (B) in theorem under_top : under A (⊤ : Ideal B) = ⊤ := comap_top @@ -154,7 +163,7 @@ theorem LiesOver.of_eq_map_equiv [P.LiesOver p] {E : Type*} [EquivLike E B C] exact of_eq_comap p (AlgEquivClass.toAlgEquiv σ : B ≃ₐ[A] C).symm h variable {p} in -instance LiesOver.smul [h : P.LiesOver p] : (g • P).LiesOver p := +instance LiesOver.smul [SMulCommClass G A B] [h : P.LiesOver p] : (g • P).LiesOver p := ⟨h.over.trans (under_smul A P g).symm⟩ variable (P) (Q) @@ -185,6 +194,12 @@ theorem LiesOver.trans [𝔓.LiesOver P] [P.LiesOver p] : 𝔓.LiesOver p where theorem LiesOver.tower_bot [hp : 𝔓.LiesOver p] [hP : 𝔓.LiesOver P] : P.LiesOver p where over := by rw [𝔓.over_def p, 𝔓.over_def P, under_under] +instance [𝔓.LiesOver P] : 𝔓.LiesOver (P.under A) := + .trans 𝔓 P (P.under A) + +instance [𝔓.LiesOver P] : P.LiesOver (𝔓.under A) := + .tower_bot 𝔓 P (𝔓.under A) + /-- Consider the following commutative diagram of ring maps ``` diff --git a/Mathlib/RingTheory/Ideal/Pointwise.lean b/Mathlib/RingTheory/Ideal/Pointwise.lean index 46e853d9691a61..cf3bbdbd200298 100644 --- a/Mathlib/RingTheory/Ideal/Pointwise.lean +++ b/Mathlib/RingTheory/Ideal/Pointwise.lean @@ -27,13 +27,13 @@ Where possible, try to keep them in sync. open Set -variable {M R : Type*} +variable {M N R : Type*} namespace Ideal section Monoid -variable [Monoid M] [Semiring R] [MulSemiringAction M R] +variable [Monoid M] [Monoid N] [Semiring R] [MulSemiringAction M R] [MulSemiringAction N R] /-- The action on an ideal corresponding to applying the action to every element. @@ -67,6 +67,13 @@ theorem pointwise_smul_def {a : M} (S : Ideal R) : a • S = S.map (MulSemiringAction.toRingHom _ _ a) := rfl +instance [SMul M N] [IsScalarTower M N R] : IsScalarTower M N (Ideal R) where + smul_assoc x y z := by + simp_rw [pointwise_smul_def, map_map] + congr + ext + simp + -- note: unlike with `Subring`, `pointwise_smul_toAddSubgroup` wouldn't be true theorem smul_mem_pointwise_smul (m : M) (r : R) (S : Ideal R) : r ∈ S → m • r ∈ m • S := diff --git a/Mathlib/RingTheory/Ideal/Prod.lean b/Mathlib/RingTheory/Ideal/Prod.lean index a35ddec7659af5..2f9f38b8b60868 100644 --- a/Mathlib/RingTheory/Ideal/Prod.lean +++ b/Mathlib/RingTheory/Ideal/Prod.lean @@ -36,6 +36,11 @@ theorem coe_prod (I : Ideal R) (J : Ideal S) : ↑(prod I J) = (I ×ˢ J : Set ( theorem mem_prod {x : R × S} : x ∈ prod I J ↔ x.1 ∈ I ∧ x.2 ∈ J := Iff.rfl +@[simp] +theorem _root_.RingHom.ker_prodMap {T U : Type*} [Semiring T] [Semiring U] (f : R →+* S) + (g : T →+* U) : RingHom.ker (f.prodMap g) = (RingHom.ker f).prod (RingHom.ker g) := by + ext ⟨⟩; simp + @[simp] theorem prod_top_top : prod (⊤ : Ideal R) (⊤ : Ideal S) = ⊤ := Ideal.ext <| by simp diff --git a/Mathlib/RingTheory/Ideal/Quotient/HasFiniteQuotients.lean b/Mathlib/RingTheory/Ideal/Quotient/HasFiniteQuotients.lean index d08ae5b3b4f52a..656fc378d3e3d2 100644 --- a/Mathlib/RingTheory/Ideal/Quotient/HasFiniteQuotients.lean +++ b/Mathlib/RingTheory/Ideal/Quotient/HasFiniteQuotients.lean @@ -166,7 +166,7 @@ instance : HasFiniteQuotients ℤ where exact inferInstanceAs <| Finite (ℤ ⧸ Ideal.span {n}) /-- A domain that is finitely generated has finite quotients. -/ -instance [IsDomain R] [AddGroup.FG R] : HasFiniteQuotients R := - of_module_finite ℤ R +instance [IsDomain R] [Module.Finite ℤ R] : HasFiniteQuotients R := + .of_module_finite ℤ R end Ring.HasFiniteQuotients diff --git a/Mathlib/RingTheory/Ideal/Quotient/Operations.lean b/Mathlib/RingTheory/Ideal/Quotient/Operations.lean index 996128bf8ca4ef..49867700683d63 100644 --- a/Mathlib/RingTheory/Ideal/Quotient/Operations.lean +++ b/Mathlib/RingTheory/Ideal/Quotient/Operations.lean @@ -566,7 +566,7 @@ lemma _root_.AlgHom.liftOfSurjective_apply (f : A →ₐ[R] B) (hf : Function.Su (g : A →ₐ[R] C) (H : RingHom.ker f.toRingHom ≤ RingHom.ker g.toRingHom) (x) : AlgHom.liftOfSurjective f hf g H (f x) = g x := by dsimp [AlgHom.liftOfSurjective] - erw [AlgEquiv.coe_algHom] -- fixed after #21031 + erw [AlgEquiv.coe_toAlgHom] -- fixed after #21031 rw [Ideal.quotientKerAlgEquivOfSurjective_symm_apply] rfl diff --git a/Mathlib/RingTheory/Ideal/Quotient/Over.lean b/Mathlib/RingTheory/Ideal/Quotient/Over.lean new file mode 100644 index 00000000000000..8758ad62465ed9 --- /dev/null +++ b/Mathlib/RingTheory/Ideal/Quotient/Over.lean @@ -0,0 +1,38 @@ +/- +Copyright (c) 2026 Andrew Yang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Andrew Yang +-/ +module + +public import Mathlib.Data.Set.Card +public import Mathlib.RingTheory.Ideal.Over + +/-! # Lemmas about `primesOver` in quotient rings. -/ + +@[expose] public section + +variable {R S T : Type*} [CommRing R] [CommRing S] [CommRing T] [Algebra R S] [Algebra R T] + +/-- Given a prime `P` of `R` and an ideal `I` in an `R`-algebra `S`. +Suppose we can find a prime `P'` over `P`, not containing `I`, +then the number of primes of `S / I` over `P` +is strictly less than primes of `S` over `P` (provided they are finite). + +The lemma is stated in terms of surjections for syntactic generality. -/ +lemma Ideal.ncard_primesOver_lt_of_not_le + (f : S →ₐ[R] T) (Hf : Function.Surjective f) + (P : Ideal R) (P' : Ideal S) [P'.IsPrime] [P'.LiesOver P] + (hkP' : ¬ RingHom.ker f.toRingHom ≤ P') (H : (P.primesOver S).Finite) : + (P.primesOver T).ncard < (P.primesOver S).ncard := by + rw [← Set.ncard_image_of_injective _ (Ideal.comap_injective_of_surjective _ Hf)] + refine Set.ncard_lt_ncard (Set.ssubset_iff_exists.mpr ⟨?_, P', ⟨‹_›, ‹_›⟩, ?_⟩) H + · rintro _ ⟨q, ⟨_, _⟩, rfl⟩ + exact ⟨inferInstance, inferInstanceAs ((q.comap f).LiesOver _)⟩ + · rintro ⟨q, ⟨_, _⟩, rfl⟩; exact hkP' (Ideal.ker_le_comap _) + +lemma Ideal.ncard_primesOver_quotient_singleton_lt_of_notMem + (P : Ideal R) (e : S) (P' : Ideal S) [P'.IsPrime] [P'.LiesOver P] + (heP' : e ∉ P') (H : (P.primesOver S).Finite) : + (P.primesOver (S ⧸ Ideal.span {e})).ncard < (P.primesOver S).ncard := + Ideal.ncard_primesOver_lt_of_not_le _ (Ideal.Quotient.mkₐ_surjective R _) _ P' (by simpa) H diff --git a/Mathlib/RingTheory/Ideal/UFD.lean b/Mathlib/RingTheory/Ideal/UFD.lean new file mode 100644 index 00000000000000..5581830415eec3 --- /dev/null +++ b/Mathlib/RingTheory/Ideal/UFD.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2026 Yongle Hu. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Yongle Hu +-/ +module + +public import Mathlib.RingTheory.Ideal.KrullsHeightTheorem +public import Mathlib.RingTheory.Localization.Away.Lemmas +public import Mathlib.RingTheory.UniqueFactorizationDomain.Localization + +/-! +# UFD criteria via height `1` prime ideals and localization + +## Main results +* `UniqueFactorizationMonoid.iff_forall_isPrincipal_of_height_eq_one` : Let `R` be a + Noetherian domain. Then `R` is a UFD if and only if every height `1` prime ideal is principal. + +* `UniqueFactorizationMonoid.iff_localizationAway_of_prime` : Let `R` be a Noetherian domain, + `x ∈ R` be a prime element. Then `R` is a UFD if and only if `Rₓ` is a UFD. +-/ + +public section + +variable {R : Type*} [CommRing R] [IsDomain R] + +namespace Ideal + +variable [WfDvdMonoid R] {x : R} (hx : Prime x) {p : Ideal R} [p.IsPrime] (hxp : x ∉ p) + +include hx hxp + +theorem isPrincipal_of_isPrincipal_isLocalizationAway_of_prime + (S : Type*) [CommRing S] [Algebra R S] [IsLocalization.Away x S] + (hp : (map (algebraMap R S) p).IsPrincipal) : p.IsPrincipal := by + have := (disjoint_powers_iff_notMem_of_isPrime x).mpr hxp + by_cases hpbot : p = ⊥ + · simp [hpbot, bot_isPrincipal] + · have hi := IsLocalization.injective S (powers_le_nonZeroDivisors_of_noZeroDivisors hx.ne_zero) + have hpb : map (algebraMap R S) p ≠ ⊥ := by simp [Ideal.map_eq_bot_iff_of_injective hi, hpbot] + obtain ⟨g, hg⟩ := hp + have hg0 : g ≠ 0 := fun hg0 ↦ hpb <| by simp [hg0, hg] + obtain ⟨a, n, hxa, hag⟩ := exists_reduced_fraction' x S hg0 hx.irreducible + have hu : IsUnit (selfZPow x S n) := + IsUnit.of_mul_eq_one (selfZPow x S (- n)) (selfZPow_mul_neg x S n) + refine ⟨a, Ideal.eq_of_map_algebraMap_le S x ?_ (by simp [IsPrime.mul_mem_left_iff hxp]) ?_⟩ + · simp [hg, map_span, ← span_singleton_mul_left_unit hu (algebraMap R S a), hag] + · intro y hy + rw [mem_span_singleton] at hy ⊢ + exact (hx.left_dvd_or_dvd_right_of_dvd_mul hy).resolve_left hxa + +theorem isPrincipal_of_isPrincipal_localizationAway_of_prime + (hp : (map (algebraMap R (Localization.Away x)) p).IsPrincipal) : p.IsPrincipal := + p.isPrincipal_of_isPrincipal_isLocalizationAway_of_prime hx hxp (Localization.Away x) hp + +end Ideal + +namespace UniqueFactorizationMonoid + +theorem isPrincipal_of_height_eq_one [UniqueFactorizationMonoid R] + {p : Ideal R} [p.IsPrime] (hph : p.height = 1) : p.IsPrincipal := by + have hpn : p ≠ ⊥ := p.ne_bot_of_height_eq_one hph + obtain ⟨x, hxmem, hxp⟩ := Ideal.IsPrime.exists_mem_prime_of_ne_bot ‹_› hpn + exact ⟨x, p.eq_span_singleton_of_height_eq_one hph hxmem hxp⟩ + +variable [IsNoetherianRing R] + +theorem of_forall_isPrincipal_of_height_eq_one + (h : ∀ (p : Ideal R) [p.IsPrime], p.height = 1 → p.IsPrincipal) : + UniqueFactorizationMonoid R := by + rw [iff_exists_prime_mem_of_isPrime] + intro I hIn _ + rcases I.ne_bot_iff.mp hIn with ⟨x, hxI, hx0⟩ + rcases Ideal.exists_minimalPrimes_le (I.span_singleton_le_iff_mem.mpr hxI) with ⟨p, hpmin, hpl⟩ + have : p.IsPrime := hpmin.isPrime + have hpn : p ≠ ⊥ := fun hpb ↦ hx0 <| + Ideal.span_singleton_eq_bot.mp <| bot_unique (hpmin.le.trans_eq hpb) + have hpp : p.IsPrincipal := h p <| le_antisymm + (Ideal.height_le_one_of_isPrincipal_of_mem_minimalPrimes _ p hpmin) + (by simpa [Order.one_le_iff_ne_zero]) + exact ⟨hpp.generator p, hpl (hpp.generator_mem p), hpp.prime_generator_of_isPrime p hpn⟩ + +/-- Let `R` be a Noetherian domain. Then `R` is a UFD if and only if every height `1` prime ideal is + principal. -/ +@[stacks 0AFT] +theorem iff_forall_isPrincipal_of_height_eq_one : + UniqueFactorizationMonoid R ↔ ∀ (p : Ideal R) [p.IsPrime], p.height = 1 → p.IsPrincipal := + ⟨fun _ _ _ ↦ isPrincipal_of_height_eq_one, of_forall_isPrincipal_of_height_eq_one⟩ + +theorem iff_of_isLocalizationAway_of_prime {x : R} (hx : Prime x) + (S : Type*) [CommRing S] [Algebra R S] [IsLocalization.Away x S] : + UniqueFactorizationMonoid R ↔ UniqueFactorizationMonoid S := by + have : IsDomain S := IsLocalization.Away.isDomain S hx.ne_zero + refine ⟨fun _ ↦ of_isLocalization (Submonoid.powers x) S, fun _ ↦ ?_⟩ + rw [iff_forall_isPrincipal_of_height_eq_one] + intro p hp h1 + by_cases hxp : x ∈ p + · exact ⟨x, p.eq_span_singleton_of_height_eq_one h1 hxp hx⟩ + · have hd := by rwa [← Ideal.disjoint_powers_iff_notMem_of_isPrime x] at hxp + have := IsLocalization.isPrime_of_isPrime_disjoint (Submonoid.powers x) S p hp hd + refine p.isPrincipal_of_isPrincipal_isLocalizationAway_of_prime hx hxp S + (isPrincipal_of_height_eq_one ?_) + rw [← IsLocalization.height_under (Submonoid.powers x), + IsLocalization.under_map_of_isPrime_disjoint (Submonoid.powers x) S hp hd, h1] + +/-- Let `R` be a Noetherian domain, `x ∈ R` be a prime element. Then `R` is a UFD if and only if + `Rₓ` is a UFD. -/ +theorem iff_localizationAway_of_prime {x : R} (hx : Prime x) : + UniqueFactorizationMonoid R ↔ UniqueFactorizationMonoid (Localization.Away x) := + iff_of_isLocalizationAway_of_prime hx (Localization.Away x) + +end UniqueFactorizationMonoid diff --git a/Mathlib/RingTheory/Idempotents.lean b/Mathlib/RingTheory/Idempotents.lean index 32e9eb9f8daab6..815c057d4267b7 100644 --- a/Mathlib/RingTheory/Idempotents.lean +++ b/Mathlib/RingTheory/Idempotents.lean @@ -8,6 +8,7 @@ module public import Mathlib.Algebra.BigOperators.Fin public import Mathlib.Algebra.Ring.GeomSum public import Mathlib.RingTheory.Ideal.Quotient.Operations +public import Mathlib.Tactic.LinearCombination /-! @@ -478,6 +479,36 @@ noncomputable def AlgEquiv.prodQuotientOfIsIdempotentElem AlgEquiv.ofBijective ((Ideal.Quotient.mkₐ _ _).prod (Ideal.Quotient.mkₐ _ _)) <| RingHom.prod_bijective_of_isIdempotentElem he hf hef₁ hef₂ +/-- One can lift a family of complete orthogonal idempotents of `R/e₀` to get one on `R`. + +Note that the lemma itself is stated in terms of surjections (where `T = S / I`) +instead for syntactic generality. -/ +lemma CompleteOrthogonalIdempotents.exists_eq_comp_of_ker_eq_span + (f : R →+* S) (e₀ : R) (he₀ : IsIdempotentElem e₀) (hfe₀ : RingHom.ker f = .span {e₀}) + (e : I → S) (he : CompleteOrthogonalIdempotents e) (hef : ∀ i, e i ∈ f.range) : + ∃ e', CompleteOrthogonalIdempotents (Option.rec e₀ e') ∧ e = f ∘ e' := by + choose e' he' using hef + choose k hk using fun i ↦ Ideal.mem_span_singleton.mp + (hfe₀.le (show f (e' i * e' i - e' i) = 0 by simp [he', (he.1.1 i).eq])) + refine ⟨(1 - e₀) • e', ⟨⟨Option.rec he₀ fun i ↦ ?_, ?_⟩, ?_⟩, ?_⟩ + · rintro (_|i) (_|j) h + · simp at h + · dsimp; linear_combination - he₀.eq * e' j + · dsimp; linear_combination - he₀.eq * e' i + · obtain ⟨k, hk⟩ := Ideal.mem_span_singleton.mp + (hfe₀.le (show f (e' i * e' j) = 0 by simp [he', he.1.2 (by simpa using h)])) + dsimp + rw [mul_mul_mul_comm, hk, he₀.one_sub.eq, ← mul_assoc, he₀.one_sub_mul_self, zero_mul] + · obtain ⟨k, hk⟩ := Ideal.mem_span_singleton.mp + (hfe₀.le (show f (∑ i, e' i - 1) = 0 by simpa [he', sub_eq_zero] using he.2)) + simp only [Fintype.sum_option, Pi.smul_apply, smul_eq_mul, ← Finset.mul_sum, + sub_eq_iff_eq_add.mp hk] + linear_combination - he₀.eq * k + · have : f e₀ = 0 := by simpa using hfe₀.ge (Ideal.mem_span_singleton_self _) + aesop + · dsimp [IsIdempotentElem] + linear_combination congr($(he₀.eq) * ((e' i) ^ 2 - k i) + (1 - e₀) * $(hk i)) + end CommRing section corner @@ -589,4 +620,14 @@ def CompleteOrthogonalIdempotents.ringEquivOfComm [CommSemiring R] (he : CompleteOrthogonalIdempotents e) : R ≃+* Π i, (he.idem i).Corner := he.ringEquivOfIsMulCentral fun _ ↦ Semigroup.mem_center_iff.mpr fun _ ↦ mul_comm .. +lemma Ideal.mem_map_span_singleton_iff_of_isIdempotentElem + [CommRing R] {e r : R} (he : IsIdempotentElem e) {I : Ideal R} : + Ideal.Quotient.mk _ r ∈ I.map (Ideal.Quotient.mk (Ideal.span {e})) ↔ (1 - e) * r ∈ I := by + simp only [Ideal.mem_map_iff_of_surjective _ Ideal.Quotient.mk_surjective, + Ideal.Quotient.mk_eq_mk_iff_sub_mem, Ideal.mem_span_singleton] + refine ⟨?_, fun H ↦ ⟨_, H, by simp [sub_mul]⟩⟩ + intro ⟨s, hs, t, hrst⟩ + convert I.mul_mem_left (1 - e) hs using 1 + linear_combination he.eq * t - (1 - e) * hrst + end corner diff --git a/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean b/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean index 13ef5f6e66b7ec..865dc6869a9008 100644 --- a/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean +++ b/Mathlib/RingTheory/IntegralClosure/IsIntegralClosure/Basic.lean @@ -599,6 +599,9 @@ theorem isIntegral_quotientMap_iff {I : Ideal S} : refine this ▸ RingHom.IsIntegral.trans g (Ideal.quotientMap I f le_rfl) ?_ h exact g.isIntegral_of_surjective Ideal.Quotient.mk_surjective +theorem RingHom.IsIntegral.kerLift {f : S →+* T} (hf : f.IsIntegral) : f.kerLift.IsIntegral := + RingHom.IsIntegral.tower_top (Ideal.Quotient.mk (RingHom.ker f)) f.kerLift hf + theorem RingHom.IsIntegral.isLocalHom {f : R →+* S} (hf : f.IsIntegral) (inj : Function.Injective f) : IsLocalHom f where map_nonunit a ha := by diff --git a/Mathlib/RingTheory/Invariant/Basic.lean b/Mathlib/RingTheory/Invariant/Basic.lean index 83972a5c02b355..e06ded1554491a 100644 --- a/Mathlib/RingTheory/Invariant/Basic.lean +++ b/Mathlib/RingTheory/Invariant/Basic.lean @@ -5,9 +5,9 @@ Authors: Thomas Browning -/ module +public import Mathlib.FieldTheory.Fixed +public import Mathlib.RingTheory.Ideal.GoingUp public import Mathlib.RingTheory.Invariant.Defs -public import Mathlib.RingTheory.IntegralClosure.IntegralRestrict -public import Mathlib.RingTheory.LocalRing.ResidueField.Ideal /-! # Invariant Extensions of Rings @@ -41,54 +41,11 @@ If `Q` is a prime ideal of `B` lying over a prime ideal `P` of `A`, then @[expose] public section -open scoped Pointwise +-- this file should not import any field theory beyond the contents of `FieldTheory/Fixed.lean` +-- material involving Galois theory should be placed in `RingTheory/Invariant/Galois.lean` +assert_not_exists IntermediateField.adjoin -section Galois - -variable (A K L B : Type*) [CommRing A] [CommRing B] [Field K] [Field L] - [Algebra A K] [Algebra B L] [IsFractionRing A K] [IsFractionRing B L] - [Algebra A B] [Algebra K L] [Algebra A L] [IsScalarTower A K L] [IsScalarTower A B L] - [IsIntegrallyClosed A] [IsIntegralClosure B A L] - -/-- In the AKLB setup, the Galois group of `L/K` acts on `B`. -/ -@[implicit_reducible] -noncomputable def IsIntegralClosure.MulSemiringAction [Algebra.IsAlgebraic K L] : - MulSemiringAction Gal(L/K) B := - MulSemiringAction.compHom B (galRestrict A K L B).toMonoidHom - -instance [Algebra.IsAlgebraic K L] : let := IsIntegralClosure.MulSemiringAction A K L B - SMulDistribClass Gal(L/K) B L := - let := IsIntegralClosure.MulSemiringAction A K L B - ⟨fun g b l ↦ by - simp only [Algebra.smul_def, smul_mul', mul_eq_mul_right_iff] - exact Or.inl (algebraMap_galRestrictHom_apply A K L B g b).symm⟩ - -/-- In the AKLB setup, every fixed point of `B` lies in the image of `A`. -/ -theorem Algebra.isInvariant_of_isGalois [FiniteDimensional K L] [h : IsGalois K L] : - letI := IsIntegralClosure.MulSemiringAction A K L B - Algebra.IsInvariant A B Gal(L/K) := by - replace h := ((IsGalois.tfae (F := K) (E := L)).out 0 1).mp h - letI := IsIntegralClosure.MulSemiringAction A K L B - refine ⟨fun b hb ↦ ?_⟩ - replace hb : algebraMap B L b ∈ IntermediateField.fixedField (⊤ : Subgroup Gal(L/K)) := by - rintro ⟨g, -⟩ - exact (algebraMap_galRestrict_apply A g b).symm.trans (congrArg (algebraMap B L) (hb g)) - rw [h, IntermediateField.mem_bot] at hb - obtain ⟨k, hk⟩ := hb - have hb : IsIntegral A b := IsIntegralClosure.isIntegral A L b - rw [← isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective B L), ← hk, - isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective K L)] at hb - obtain ⟨a, rfl⟩ := IsIntegrallyClosed.algebraMap_eq_of_integral hb - rw [← IsScalarTower.algebraMap_apply, IsScalarTower.algebraMap_apply A B L, - (FaithfulSMul.algebraMap_injective B L).eq_iff] at hk - exact ⟨a, hk⟩ - -/-- A variant of `Algebra.isInvariant_of_isGalois`, replacing `Gal(L/K)` by `Aut(B/A)`. -/ -theorem Algebra.isInvariant_of_isGalois' [FiniteDimensional K L] [IsGalois K L] : - Algebra.IsInvariant A B (B ≃ₐ[A] B) := - ⟨fun b h ↦ (isInvariant_of_isGalois A K L B).1 b (fun g ↦ h (galRestrict A K L B g))⟩ - -end Galois +open scoped Pointwise section Quotient @@ -515,81 +472,6 @@ lemma Ideal.Quotient.exists_algEquiv_fixedPoint_quotient_under refine .trans ?_ (σ.apply_symm_apply _) rw [← h₂, ← e, h₁] -namespace Ideal.IsFractionRing - -variable [P.IsPrime] [Q.IsPrime] (K L : Type*) [Field K] [Field L] [Algebra K L] - [Algebra (A ⧸ P) K] [IsFractionRing (A ⧸ P) K] [Algebra (B ⧸ Q) L] [IsFractionRing (B ⧸ Q) L] - [Algebra (A ⧸ P) L] [IsScalarTower (A ⧸ P) (B ⧸ Q) L] [IsScalarTower (A ⧸ P) K L] - -open Polynomial in -include P Q G in -lemma normal : Normal K L := by - have := Algebra.IsInvariant.isIntegral A B G - have := isAlgebraic_of_isFractionRing (A ⧸ P) (B ⧸ Q) K L - constructor - intro x - obtain ⟨x, y, hy, rfl⟩ := IsFractionRing.div_surjective (B ⧸ Q) x - obtain ⟨b, a, ha, h⟩ := (Algebra.IsAlgebraic.isAlgebraic (R := A ⧸ P) y).exists_smul_eq_mul x hy - obtain ⟨a, rfl⟩ := Quotient.mk_surjective a - obtain ⟨b, rfl⟩ := Quotient.mk_surjective b - simp_rw [← Quotient.algebraMap_eq] at * - cases nonempty_fintype G - obtain ⟨p, hp, -, h_monic⟩ := lifts_and_natDegree_eq_and_monic - (Algebra.IsInvariant.charpoly_mem_lifts A B G b) (MulSemiringAction.monic_charpoly ..) - have h_eval : p.aeval b = 0 := by - rw [← eval_map_algebraMap, hp, MulSemiringAction.eval_charpoly] - let q := p.comp (C a * X) - let d := (algebraMap (B ⧸ Q) L) x / (algebraMap (B ⧸ Q) L) y - have comm₁ : (algebraMap K L).comp (algebraMap (A ⧸ P) K) = - (algebraMap (B ⧸ Q) L).comp (algebraMap (A ⧸ P) (B ⧸ Q)) := by - simp_rw [← IsScalarTower.algebraMap_eq] - have comm₂ : (algebraMap (A ⧸ P) (B ⧸ Q)).comp (algebraMap A (A ⧸ P)) = - (algebraMap B (B ⧸ Q)).comp (algebraMap A B) := by - simp_rw [← IsScalarTower.algebraMap_eq] - replace h_eval : ((q.map (algebraMap A (A ⧸ P))).map (algebraMap (A ⧸ P) K)).aeval d = 0 := by - simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, aeval_comp, aeval_mul, aeval_C, aeval_X, - ← RingHom.comp_apply, ← RingHom.comp_assoc, comm₁, RingHom.comp_apply, d, mul_div, ← map_mul] - rw [← Algebra.smul_def, h, map_mul, mul_div_cancel_left₀ _ (by simpa using hy), - aeval_map_algebraMap, aeval_algebraMap_apply, aeval_map_algebraMap, aeval_algebraMap_apply, - h_eval, map_zero, map_zero] - replace h_splits : (p.map (algebraMap A B)).Splits := by - rw [hp] - exact MulSemiringAction.splits_charpoly G b - refine .of_dvd ?_ ?_ (map_dvd (algebraMap K L) (minpoly.dvd K d h_eval)) - · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X] - refine .comp_of_degree_le_one ?_ (degree_C_mul_X_le _) - rw [Polynomial.map_map, Polynomial.map_map, comm₁, RingHom.comp_assoc, comm₂, - ← RingHom.comp_assoc, ← Polynomial.map_map] - apply h_splits.map - · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, Polynomial.map_map] - exact mt (comp_C_mul_X_eq_zero_iff (by simpa)).mp (map_monic_ne_zero h_monic) - -include P Q in -lemma finite_of_isInvariant [SMulCommClass G A B] [Algebra.IsSeparable K L] : - Module.Finite K L := by - have : IsGalois K L := { __ := normal G P Q K L } - have := Finite.of_surjective _ (IsFractionRing.stabilizerHom_surjective G P Q K L) - apply IsGalois.finiteDimensional_of_finite - -end Ideal.IsFractionRing - -attribute [local instance] Ideal.Quotient.field in -include G in -/-- -For any domain `k` containing `B ⧸ Q`, -any endomorphism of `k` can be restricted to an endomorphism of `B ⧸ Q`. -/ -lemma Ideal.Quotient.normal [P.IsMaximal] [Q.IsMaximal] : - Normal (A ⧸ P) (B ⧸ Q) := - IsFractionRing.normal G P Q (A ⧸ P) (B ⧸ Q) - -attribute [local instance] Ideal.Quotient.field in -include G in -/-- If the extension `B/Q` over `A/P` is separable, then it is finite dimensional. -/ -lemma Ideal.Quotient.finite_of_isInvariant [P.IsMaximal] [Q.IsMaximal] - [SMulCommClass G A B] [Algebra.IsSeparable (A ⧸ P) (B ⧸ Q)] : - Module.Finite (A ⧸ P) (B ⧸ Q) := - IsFractionRing.finite_of_isInvariant G P Q (A ⧸ P) (B ⧸ Q) - end normal namespace IsFractionRing diff --git a/Mathlib/RingTheory/Invariant/Galois.lean b/Mathlib/RingTheory/Invariant/Galois.lean new file mode 100644 index 00000000000000..c86f93056fb6d5 --- /dev/null +++ b/Mathlib/RingTheory/Invariant/Galois.lean @@ -0,0 +1,152 @@ +/- +Copyright (c) 2024 Thomas Browning. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Thomas Browning +-/ +module + +public import Mathlib.RingTheory.Invariant.Basic +public import Mathlib.RingTheory.IntegralClosure.IntegralRestrict + +/-! +# Invariant Extensions of Rings and Galois Theory + +Given an extension of rings `B/A` and an action of `G` on `B`, the predicate +`Algebra.IsInvariant A B G` states that every fixed point of `B` lies in the image of `A`. + +This file relates this predicate `Algebra.IsInvariant` to Galois theory. +-/ + +@[expose] public section + +open scoped Pointwise + +section Galois + +variable (A K L B : Type*) [CommRing A] [CommRing B] [Field K] [Field L] + [Algebra A K] [Algebra B L] [IsFractionRing A K] [IsFractionRing B L] + [Algebra A B] [Algebra K L] [Algebra A L] [IsScalarTower A K L] [IsScalarTower A B L] + [IsIntegrallyClosed A] [IsIntegralClosure B A L] + +/-- In the AKLB setup, the Galois group of `L/K` acts on `B`. -/ +@[implicit_reducible] +noncomputable def IsIntegralClosure.MulSemiringAction [Algebra.IsAlgebraic K L] : + MulSemiringAction Gal(L/K) B := + MulSemiringAction.compHom B (galRestrict A K L B).toMonoidHom + +instance [Algebra.IsAlgebraic K L] : let := IsIntegralClosure.MulSemiringAction A K L B + SMulDistribClass Gal(L/K) B L := + let := IsIntegralClosure.MulSemiringAction A K L B + ⟨fun g b l ↦ by + simp only [Algebra.smul_def, smul_mul', mul_eq_mul_right_iff] + exact Or.inl (algebraMap_galRestrictHom_apply A K L B g b).symm⟩ + +/-- In the AKLB setup, every fixed point of `B` lies in the image of `A`. -/ +theorem Algebra.isInvariant_of_isGalois [FiniteDimensional K L] [h : IsGalois K L] : + letI := IsIntegralClosure.MulSemiringAction A K L B + Algebra.IsInvariant A B Gal(L/K) := by + replace h := ((IsGalois.tfae (F := K) (E := L)).out 0 1).mp h + letI := IsIntegralClosure.MulSemiringAction A K L B + refine ⟨fun b hb ↦ ?_⟩ + replace hb : algebraMap B L b ∈ IntermediateField.fixedField (⊤ : Subgroup Gal(L/K)) := by + rintro ⟨g, -⟩ + exact (algebraMap_galRestrict_apply A g b).symm.trans (congrArg (algebraMap B L) (hb g)) + rw [h, IntermediateField.mem_bot] at hb + obtain ⟨k, hk⟩ := hb + have hb : IsIntegral A b := IsIntegralClosure.isIntegral A L b + rw [← isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective B L), ← hk, + isIntegral_algebraMap_iff (FaithfulSMul.algebraMap_injective K L)] at hb + obtain ⟨a, rfl⟩ := IsIntegrallyClosed.algebraMap_eq_of_integral hb + rw [← IsScalarTower.algebraMap_apply, IsScalarTower.algebraMap_apply A B L, + (FaithfulSMul.algebraMap_injective B L).eq_iff] at hk + exact ⟨a, hk⟩ + +/-- A variant of `Algebra.isInvariant_of_isGalois`, replacing `Gal(L/K)` by `Aut(B/A)`. -/ +theorem Algebra.isInvariant_of_isGalois' [FiniteDimensional K L] [IsGalois K L] : + Algebra.IsInvariant A B (B ≃ₐ[A] B) := + ⟨fun b h ↦ (isInvariant_of_isGalois A K L B).1 b (fun g ↦ h (galRestrict A K L B g))⟩ + +end Galois + +section normal + +variable {A B : Type*} [CommRing A] [CommRing B] [Algebra A B] + (G : Type*) [Finite G] [Group G] [MulSemiringAction G B] [Algebra.IsInvariant A B G] + (P : Ideal A) (Q : Ideal B) [Q.LiesOver P] + +namespace Ideal.IsFractionRing + +variable [P.IsPrime] [Q.IsPrime] (K L : Type*) [Field K] [Field L] [Algebra K L] + [Algebra (A ⧸ P) K] [IsFractionRing (A ⧸ P) K] [Algebra (B ⧸ Q) L] [IsFractionRing (B ⧸ Q) L] + [Algebra (A ⧸ P) L] [IsScalarTower (A ⧸ P) (B ⧸ Q) L] [IsScalarTower (A ⧸ P) K L] + +open Polynomial in +include P Q G in +lemma normal : Normal K L := by + have := Algebra.IsInvariant.isIntegral A B G + have := isAlgebraic_of_isFractionRing (A ⧸ P) (B ⧸ Q) K L + constructor + intro x + obtain ⟨x, y, hy, rfl⟩ := IsFractionRing.div_surjective (B ⧸ Q) x + obtain ⟨b, a, ha, h⟩ := (Algebra.IsAlgebraic.isAlgebraic (R := A ⧸ P) y).exists_smul_eq_mul x hy + obtain ⟨a, rfl⟩ := Quotient.mk_surjective a + obtain ⟨b, rfl⟩ := Quotient.mk_surjective b + simp_rw [← Quotient.algebraMap_eq] at * + cases nonempty_fintype G + obtain ⟨p, hp, -, h_monic⟩ := lifts_and_natDegree_eq_and_monic + (Algebra.IsInvariant.charpoly_mem_lifts A B G b) (MulSemiringAction.monic_charpoly ..) + have h_eval : p.aeval b = 0 := by + rw [← eval_map_algebraMap, hp, MulSemiringAction.eval_charpoly] + let q := p.comp (C a * X) + let d := (algebraMap (B ⧸ Q) L) x / (algebraMap (B ⧸ Q) L) y + have comm₁ : (algebraMap K L).comp (algebraMap (A ⧸ P) K) = + (algebraMap (B ⧸ Q) L).comp (algebraMap (A ⧸ P) (B ⧸ Q)) := by + simp_rw [← IsScalarTower.algebraMap_eq] + have comm₂ : (algebraMap (A ⧸ P) (B ⧸ Q)).comp (algebraMap A (A ⧸ P)) = + (algebraMap B (B ⧸ Q)).comp (algebraMap A B) := by + simp_rw [← IsScalarTower.algebraMap_eq] + replace h_eval : ((q.map (algebraMap A (A ⧸ P))).map (algebraMap (A ⧸ P) K)).aeval d = 0 := by + simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, aeval_comp, aeval_mul, aeval_C, aeval_X, + ← RingHom.comp_apply, ← RingHom.comp_assoc, comm₁, RingHom.comp_apply, d, mul_div, ← map_mul] + rw [← Algebra.smul_def, h, map_mul, mul_div_cancel_left₀ _ (by simpa using hy), + aeval_map_algebraMap, aeval_algebraMap_apply, aeval_map_algebraMap, aeval_algebraMap_apply, + h_eval, map_zero, map_zero] + replace h_splits : (p.map (algebraMap A B)).Splits := by + rw [hp] + exact MulSemiringAction.splits_charpoly G b + refine .of_dvd ?_ ?_ (map_dvd (algebraMap K L) (minpoly.dvd K d h_eval)) + · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X] + refine .comp_of_degree_le_one ?_ (degree_C_mul_X_le _) + rw [Polynomial.map_map, Polynomial.map_map, comm₁, RingHom.comp_assoc, comm₂, + ← RingHom.comp_assoc, ← Polynomial.map_map] + apply h_splits.map + · simp_rw [q, map_comp, Polynomial.map_mul, map_C, map_X, Polynomial.map_map] + exact mt (comp_C_mul_X_eq_zero_iff (by simpa)).mp (map_monic_ne_zero h_monic) + +include P Q in +lemma finite_of_isInvariant [SMulCommClass G A B] [Algebra.IsSeparable K L] : + Module.Finite K L := by + have : IsGalois K L := { __ := normal G P Q K L } + have := Finite.of_surjective _ (IsFractionRing.stabilizerHom_surjective G P Q K L) + apply IsGalois.finiteDimensional_of_finite + +end Ideal.IsFractionRing + +attribute [local instance] Ideal.Quotient.field in +include G in +/-- +For any domain `k` containing `B ⧸ Q`, +any endomorphism of `k` can be restricted to an endomorphism of `B ⧸ Q`. -/ +lemma Ideal.Quotient.normal [P.IsMaximal] [Q.IsMaximal] : + Normal (A ⧸ P) (B ⧸ Q) := + IsFractionRing.normal G P Q (A ⧸ P) (B ⧸ Q) + +attribute [local instance] Ideal.Quotient.field in +include G in +/-- If the extension `B/Q` over `A/P` is separable, then it is finite dimensional. -/ +lemma Ideal.Quotient.finite_of_isInvariant [P.IsMaximal] [Q.IsMaximal] + [SMulCommClass G A B] [Algebra.IsSeparable (A ⧸ P) (B ⧸ Q)] : + Module.Finite (A ⧸ P) (B ⧸ Q) := + IsFractionRing.finite_of_isInvariant G P Q (A ⧸ P) (B ⧸ Q) + +end normal diff --git a/Mathlib/RingTheory/IsAdjoinRoot.lean b/Mathlib/RingTheory/IsAdjoinRoot.lean index eb6034e89f1241..07ee4861ca3196 100644 --- a/Mathlib/RingTheory/IsAdjoinRoot.lean +++ b/Mathlib/RingTheory/IsAdjoinRoot.lean @@ -649,30 +649,20 @@ theorem minpoly_eq [IsDomain R] [IsDomain S] [IsTorsionFree R S] [IsIntegrallyCl then `S` is given by adjoining a root of `minpoly R α`. Does not require that `R` is an integral domain, unlike `mkOfAdjoinEqTop`. -/ @[simps] -def mkOfAdjoinEqTop' - [Module.Finite R S] [Module.Free R S] - {α : S} (hα : Algebra.adjoin R {α} = ⊤) : +def mkOfAdjoinEqTop' [Module.Finite R S] [Module.Free R S] {α : S} (hα : Algebra.adjoin R {α} = ⊤) : IsAdjoinRootMonic S (minpoly R α) where __ : IsAdjoinRoot S (minpoly R α) := - let f := minpoly R α - have hf := minpoly.monic (Algebra.IsIntegral.isIntegral (R := R) α) - let φ : AdjoinRoot f →ₐ[R] S := - AdjoinRoot.liftAlgHom f (Algebra.ofId R S) α (minpoly.aeval R α) + have monic := minpoly.monic (Algebra.IsIntegral.isIntegral (R := R) α) + haveI := monic.free_adjoinRoot + haveI := monic.finite_adjoinRoot + let φ := AdjoinRoot.liftAlgHom _ (Algebra.ofId R S) _ (minpoly.aeval R α) IsAdjoinRoot.ofAdjoinRootEquiv <| AlgEquiv.ofBijective φ <| by - have hφ : Function.Surjective φ := by - rw [Algebra.adjoin_singleton_eq_range_aeval, AlgHom.range_eq_top] at hα - intro s; obtain ⟨p, hp⟩ := hα s - exact ⟨AdjoinRoot.mk f p, by simp [φ, ← aeval_def, hp]⟩ - haveI := hf.free_adjoinRoot; haveI := hf.finite_adjoinRoot - by_cases h : Nontrivial R - · letI e := LinearEquiv.ofFinrankEq (R := R) (AdjoinRoot f) S <| - le_antisymm (finrank_quotient_span_eq_natDegree' hf ▸ minpoly.natDegree_le α) - (LinearMap.finrank_le_finrank_of_surjective (f := φ.toLinearMap) hφ) - exact OrzechProperty.bijective_of_surjective_of_injective - e.toLinearMap φ e.injective hφ - · apply not_nontrivial_iff_subsingleton.mp at h - haveI := Module.subsingleton R (AdjoinRoot f) - exact ⟨Function.injective_of_subsingleton φ, hφ⟩ + refine OrzechProperty.bijective_of_surjective_of_finrank_le φ.toLinearMap (fun s ↦ ?_) ?_ + · rw [Algebra.adjoin_singleton_eq_range_aeval, AlgHom.range_eq_top] at hα + rcases hα s with ⟨p, hp⟩ + exact ⟨AdjoinRoot.mk (minpoly R α) p, by simp [φ, ← aeval_def, hp]⟩ + · nontriviality R + exact finrank_quotient_span_eq_natDegree' monic ▸ minpoly.natDegree_le α map := aeval α monic := minpoly.monic (Algebra.IsIntegral.isIntegral α) diff --git a/Mathlib/RingTheory/IsGaloisGroup/Basic.lean b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean new file mode 100644 index 00000000000000..8dd8974d9fb075 --- /dev/null +++ b/Mathlib/RingTheory/IsGaloisGroup/Basic.lean @@ -0,0 +1,264 @@ +/- +Copyright (c) 2025 Thomas Browning. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Thomas Browning +-/ +module + +public import Mathlib.RingTheory.IntegralClosure.IntegrallyClosed +public import Mathlib.RingTheory.Invariant.Basic +public import Mathlib.RingTheory.IsGaloisGroup.Defs + +/-! +# Galois Groups of Rings + +Given an action of a group `G` on an extension of rings `B/A`, the predicate `IsGaloisGroup G A B` +states that `G` acts faithfully on `B` with fixed ring `A`. This file develops some of the theory +of this predicate without assuming Galois theory for fields. +-/ + +@[expose] public section + +-- this file should not import any field theory beyond the contents of `FieldTheory/Fixed.lean` +-- material involving Galois theory should be placed in `FieldTheory/IsGaloisGroup.lean` +assert_not_exists IntermediateField.adjoin + +open Module + +section CommRing + +variable (G A B : Type*) [Group G] [CommSemiring A] [Semiring B] [Algebra A B] + [MulSemiringAction G B] + +variable {C : Type*} [CommSemiring C] [Algebra C B] + +variable {G} in +protected theorem Subgroup.smul_algebraMap {H : Subgroup G} [SMulCommClass H C B] {g : G} + (hg : g ∈ H) (x : C) : + g • algebraMap C B x = algebraMap C B x := + smul_algebraMap (⟨g, hg⟩ : H) x + +theorem IsGaloisGroup.smul_mem_of_normal (N : Subgroup G) [hN : N.Normal] + [hC : IsGaloisGroup N C B] (g : G) (x : C) : + g • algebraMap C B x ∈ Set.range (algebraMap C B) := by + apply hC.isInvariant.isInvariant (g • algebraMap C B x) + intro n + rw [← inv_smul_eq_iff, Subgroup.smul_def, ← mul_smul, ← mul_smul] + exact Subgroup.smul_algebraMap B (hN.conj_mem' n n.prop g) x + +@[deprecated (since := "2026-05-28")] alias smul_eq_self := Subgroup.smul_algebraMap +@[deprecated (since := "2026-05-28")] alias smul_mem_of_normal := IsGaloisGroup.smul_mem_of_normal + +end CommRing + +section Field + +variable (G A B K L : Type*) [Group G] [CommRing A] [CommRing B] [MulSemiringAction G B] + [Algebra A B] [Field K] [Field L] [Algebra K L] [Algebra A K] [Algebra B L] [Algebra A L] + [IsFractionRing A K] [IsFractionRing B L] [IsScalarTower A K L] [IsScalarTower A B L] + [MulSemiringAction G L] [SMulDistribClass G B L] + +instance [IsGaloisGroup G A B] : IsGaloisGroup G (algebraMap A B).range B where + faithful := IsGaloisGroup.faithful A + commutes := ⟨fun g ⟨a', ⟨a, ha⟩⟩ b ↦ by simp [Subring.smul_def, ← ha]⟩ + isInvariant := ⟨fun b hb ↦ by + obtain ⟨a, ha⟩ := Algebra.IsInvariant.isInvariant (A := A) b hb + exact ⟨⟨algebraMap A B a, ⟨a, rfl⟩⟩, ha⟩⟩ + +/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/ +theorem IsGaloisGroup.to_isFractionRing_of_isIntegral + [Algebra.IsIntegral A B] [hGAB : IsGaloisGroup G A B] : + IsGaloisGroup G K L where + faithful := + have := hGAB.faithful + IsFractionRing.faithfulSMul G B L + commutes := IsFractionRing.smulCommClass G A B K L + isInvariant := IsFractionRing.isInvariant_of_isIntegral G A B K L + +/-- `IsGaloisGroup` for rings implies `IsGaloisGroup` for their fraction fields. -/ +theorem IsGaloisGroup.to_isFractionRing [Finite G] [hGAB : IsGaloisGroup G A B] : + IsGaloisGroup G K L := + have := hGAB.isInvariant.isIntegral + IsGaloisGroup.to_isFractionRing_of_isIntegral G A B K L + +/-- If `B` is an integral extension of an integrally closed domain `A`, then `IsGaloisGroup` for +their fraction fields implies `IsGaloisGroup` for these rings. -/ +theorem IsGaloisGroup.of_isFractionRing [hGKL : IsGaloisGroup G K L] + [IsIntegrallyClosed A] [Algebra.IsIntegral A B] : IsGaloisGroup G A B := by + have hc (a : A) : (algebraMap K L) (algebraMap A K a) = (algebraMap B L) (algebraMap A B a) := by + simp_rw [← IsScalarTower.algebraMap_apply] + refine ⟨⟨fun h ↦ ?_⟩, ⟨fun g x y ↦ IsFractionRing.injective B L ?_⟩, ⟨fun x h ↦ ?_⟩⟩ + · have := hGKL.faithful + refine eq_of_smul_eq_smul fun (y : L) ↦ ?_ + obtain ⟨a, b, hb, rfl⟩ := IsFractionRing.div_surjective B y + simp only [smul_div₀', ← algebraMap.coe_smul', h] + · simp [Algebra.smul_def, algebraMap.coe_smul', ← hc] + · obtain ⟨b, hb⟩ := hGKL.isInvariant.isInvariant (algebraMap B L x) + (by simpa [← algebraMap.coe_smul']) + have hx : IsIntegral A (algebraMap B L x) := (Algebra.IsIntegral.isIntegral x).algebraMap + rw [← hb, isIntegral_algebraMap_iff (algebraMap K L).injective, + IsIntegrallyClosedIn.isIntegral_iff] at hx + obtain ⟨a, rfl⟩ := hx + exact ⟨a, by rwa [hc, IsFractionRing.coe_inj] at hb⟩ + +/-- If `G` is finite and `A` is integrally closed then `IsGaloisGroup G A B` is equivalent to `B/A` +being integral and the fields of fractions `Frac(B)/Frac(A)` being Galois with Galois group `G`. -/ +theorem IsGaloisGroup.iff_isFractionRing [Finite G] [IsIntegrallyClosed A] : + IsGaloisGroup G A B ↔ Algebra.IsIntegral A B ∧ IsGaloisGroup G K L := + ⟨fun h ↦ ⟨h.isInvariant.isIntegral, h.to_isFractionRing G A B K L⟩, + fun ⟨_, h⟩ ↦ h.of_isFractionRing G A B K L⟩ + +@[deprecated (since := "2026-04-20")] alias FractionRing.mulSemiringAction_of_isGaloisGroup := + IsFractionRing.mulSemiringAction + +/-- +If `G` is finite and `IsGaloisGroup G A B` with `A` and `B` domains, then `G` is also +a Galois group for `FractionRing B / FractionRing A` for the action defined by +`IsFractionRing.mulSemiringAction`. +-/ +instance IsGaloisGroup.toFractionRing [IsDomain A] [IsDomain B] [Finite G] + [IsGaloisGroup G A B] [Algebra (FractionRing A) (FractionRing B)] + [IsScalarTower A (FractionRing A) (FractionRing B)] : + letI := IsFractionRing.mulSemiringAction G B (FractionRing B) + IsGaloisGroup G (FractionRing A) (FractionRing B) := by + let := IsFractionRing.mulSemiringAction G B (FractionRing B) + apply IsGaloisGroup.to_isFractionRing G A B _ _ + +end Field + +variable (G : Type*) [Group G] + +namespace IsGaloisGroup + +section IsDomain + +variable (A B : Type*) [CommRing A] [CommRing B] [IsDomain B] [Algebra A B] [FaithfulSMul A B] + [MulSemiringAction G B] [IsGaloisGroup G A B] [Finite G] + +attribute [local instance] FractionRing.liftAlgebra in +/-- If `G` is a finite Galois group for `B/A`, then `G` is isomorphic to `Gal(B/A)`. -/ +@[simps!] noncomputable def mulEquivAlgEquiv : G ≃* Gal(B/A) := + MulEquiv.ofBijective (MulSemiringAction.toAlgAut G A B) (by + have := IsDomain.of_faithfulSMul A B + have : FaithfulSMul G B := IsGaloisGroup.faithful A + refine ⟨fun _ _ ↦ eq_of_smul_eq_smul ∘ DFunLike.ext_iff.mp, fun φ ↦ ?_⟩ + obtain ⟨g, hg⟩ := Ideal.Quotient.stabilizerHom_surjective G ⊥ ⊥ + (Ideal.Quotient.algEquivOfEqMap (⊥ : Ideal A) φ Ideal.map_bot.symm) + use g + rw [AlgEquiv.ext_iff] at hg ⊢ + exact fun x ↦ (AlgEquiv.quotientBot A B).symm.injective (hg x)) + +end IsDomain + +variable (H : Subgroup G) + +instance (R S : Type*) [CommRing R] [CommRing S] [Algebra R S] + [MulSemiringAction G S] [hGKL : IsGaloisGroup G R S] : + IsGaloisGroup H (FixedPoints.subalgebra R S H) S where + faithful := have := hGKL.faithful; inferInstance + commutes := inferInstance + isInvariant := ⟨fun x h ↦ ⟨⟨x, h⟩, rfl⟩⟩ + +section Quotient + +section Semiring + +variable (A B C : Type*) [CommSemiring A] [Semiring C] [Algebra A C] [MulSemiringAction G C] +variable (N : Subgroup G) [CommSemiring B] [Algebra B C] + +/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B`. +For `g : G` and `x : B`, `g • x` is the unique element of `B` whose image in `C` is +`g • algebraMap B C x`, see `algebraMap_smulOfNormal`. -/ +@[implicit_reducible] +noncomputable def smulOfNormal [N.Normal] [IsGaloisGroup N B C] : SMul G B where + smul g x := (smul_mem_of_normal G C N g x).choose + +@[simp] +theorem algebraMap_smulOfNormal [N.Normal] [IsGaloisGroup N B C] (g : G) (x : B) : + letI := smulOfNormal G B C + algebraMap B C (g • x) = g • algebraMap B C x := + (smul_mem_of_normal G C N g x).choose_spec + +/-- If `N` is normal and `IsGaloisGroup N B C`, the action `smulOfNormal G B C` satisfies +`SMulDistribClass G B C`. -/ +instance smulDistribClass_smulOfNormal [N.Normal] [IsGaloisGroup N B C] : + letI := smulOfNormal G B C + SMulDistribClass G B C := + let := smulOfNormal G B C + ⟨fun g b c ↦ by simp [Algebra.smul_def]⟩ + +variable [FaithfulSMul B C] + +/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then `G` acts on `B` as a +`MulSemiringAction`, via the action defined in `smulOfNormal`. -/ +@[implicit_reducible] +noncomputable def mulSemiringActionOfNormal [IsGaloisGroup N B C] [N.Normal] : + MulSemiringAction G B := by + let : SMul G B := smulOfNormal G B C N + have : SMulDistribClass G B C := smulDistribClass_smulOfNormal G B C N + exact mulSemiringActionOfSmulDistribClass B C G + +/-- If `N` is a normal subgroup of `G` and `IsGaloisGroup N B C`, then the quotient group `G ⧸ N` +acts on `B` by `(g : G ⧸ N) • x = g • x`. -/ +@[implicit_reducible] +noncomputable def mulSemiringActionQuotient [IsGaloisGroup N B C] [N.Normal] : + MulSemiringAction (G ⧸ N) B := + letI := mulSemiringActionOfNormal G B C N + { smul q x := + Quotient.liftOn' q (· • x) fun g₁ g₂ h ↦ by + apply FaithfulSMul.algebraMap_injective B C + rw [algebraMap.smul', algebraMap.smul', smul_eq_iff_eq_inv_smul, ← smul_assoc, smul_eq_mul, + Subgroup.smul_algebraMap C (by rwa [← QuotientGroup.leftRel_apply])] + one_smul x := one_smul G x + mul_smul q₁ q₂ x := Quotient.inductionOn₂' q₁ q₂ fun g h ↦ mul_smul g h x + smul_add q x y := Quotient.inductionOn' q fun g ↦ smul_add g x y + smul_zero q := Quotient.inductionOn' q fun g ↦ smul_zero g + smul_one q := Quotient.inductionOn' q fun g ↦ smul_one g + smul_mul q x y := Quotient.inductionOn' q fun g ↦ smul_mul' g x y } + +theorem mulSemiringActionQuotient_smul_def [MulSemiringAction G B] [SMulDistribClass G B C] + [IsGaloisGroup N B C] [N.Normal] (g : G) (b : B) : + letI := mulSemiringActionQuotient G B C N + (g : G ⧸ N) • b = g • b := by + let := mulSemiringActionOfNormal G B C N + refine (Quotient.liftOn'_mk'' (· • b) _ g).trans (FaithfulSMul.algebraMap_injective B C ?_) + rw [algebraMap.smul', algebraMap.smul'] + +instance isScalarTower_mulSemiringActionQuotient [MulSemiringAction G B] [SMulDistribClass G B C] + [IsGaloisGroup N B C] [N.Normal] : + letI := mulSemiringActionQuotient G B C N + IsScalarTower G (G ⧸ N) B := + let := mulSemiringActionQuotient G B C N + ⟨fun g q b ↦ Quotient.inductionOn' q fun h ↦ by + simp [mul_smul, mulSemiringActionQuotient_smul_def]⟩ + +set_option linter.defProp false in +/-- If `G` acts on `C` commuting with `A`, then the action of `G ⧸ N` on `B` commutes with `A`. -/ +@[implicit_reducible] +def smulCommClassQuotient [N.Normal] [Algebra A B] [IsScalarTower A B C] [SMulCommClass G A C] + [MulSemiringAction G B] [MulAction (G ⧸ N) B] [SMulDistribClass G B C] + [IsScalarTower G (G ⧸ N) B] : + SMulCommClass (G ⧸ N) A B := + ⟨fun g k x ↦ Quotient.inductionOn' g fun g ↦ + FaithfulSMul.algebraMap_injective B C (by + simp [algebraMap.smul, algebraMap.smul', smul_comm])⟩ + +end Semiring + +variable {K L : Type*} [Field K] [Field L] [Algebra K L] [MulSemiringAction G L] + +variable (F : IntermediateField K L) (N : Subgroup G) [N.Normal] [IsGaloisGroup N F L] + +noncomputable instance : MulSemiringAction (G ⧸ N) F := + letI := smulOfNormal G F L N + haveI := smulDistribClass_smulOfNormal G F L N + letI := mulSemiringActionOfSmulDistribClass F L G + mulSemiringActionQuotient G F L N + +instance [SMulCommClass G K L] [MulSemiringAction G F] [SMulDistribClass G F L] + [IsScalarTower G (G ⧸ N) F] : SMulCommClass (G ⧸ N) K F := + smulCommClassQuotient G K F L N + +end Quotient + +end IsGaloisGroup diff --git a/Mathlib/RingTheory/IsGaloisGroup/Defs.lean b/Mathlib/RingTheory/IsGaloisGroup/Defs.lean new file mode 100644 index 00000000000000..c0ae57e8a37fce --- /dev/null +++ b/Mathlib/RingTheory/IsGaloisGroup/Defs.lean @@ -0,0 +1,155 @@ +/- +Copyright (c) 2025 Thomas Browning. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Thomas Browning +-/ +module + +public import Mathlib.Algebra.Algebra.Subalgebra.Operations +public import Mathlib.RingTheory.Invariant.Defs + +/-! +# Predicate for Galois Groups + +Given an action of a group `G` on an extension of fields `L/K`, we introduce a predicate +`IsGaloisGroup G K L` saying that `G` acts faithfully on `L` with fixed field `K`. In particular, +we do not assume that `L` is an algebraic extension of `K`. + +## Implementation notes + +We actually define `IsGaloisGroup G A B` for extensions of rings `B/A`, with the same definition +(faithful action on `B` with fixed ring `A`). This definition turns out to axiomatize a common +setup in algebraic number theory where a Galois group `Gal(L/K)` acts on an extension of subrings +`B/A` (e.g., rings of integers). In particular, there are theorems in algebraic number theory that +naturally assume `[IsGaloisGroup G A B]` and whose statements would otherwise require assuming +`(K L : Type*) [Field K] [Field L] [Algebra K L] [IsGalois K L]` (along with predicates relating +`K` and `L` to the rings `A` and `B`) despite `K` and `L` not appearing in the conclusion. + +Unfortunately, this definition of `IsGaloisGroup G A B` for extensions of rings `B/A` is +nonstandard and clashes with other notions such as the étale fundamental group. In particular, if +`G` is finite and `A` is integrally closed, then `IsGaloisGroup G A B` is equivalent to `B/A` +being integral and the fields of fractions `Frac(B)/Frac(A)` being Galois with Galois group `G` +(see `IsGaloisGroup.iff_isFractionRing`), rather than `B/A` being étale for instance. + +But in the absence of a more suitable name, the utility of the predicate `IsGaloisGroup G A B` for +extensions of rings `B/A` seems to outweigh these terminological issues. +-/ + +@[expose] public section + +assert_not_exists IsFractionRing + +variable (G A A' B : Type*) [Group G] [CommSemiring A] [Semiring B] [Algebra A B] + [MulSemiringAction G B] + +/-- `G` is a Galois group for `L/K` if the action of `G` on `L` is faithful with fixed field `K`. +In particular, we do not assume that `L` is an algebraic extension of `K`. + +See the implementation notes in this file for the meaning of this definition in the case of rings. +-/ +class IsGaloisGroup where + faithful : FaithfulSMul G B + commutes : SMulCommClass G A B + isInvariant : Algebra.IsInvariant A B G + +namespace IsGaloisGroup + +variable {G A B} in +theorem of_mulEquiv [hG : IsGaloisGroup G A B] {H : Type*} [Group H] + [MulSemiringAction H B] (e : H ≃* G) (he : ∀ h (x : B), (e h) • x = h • x) : + IsGaloisGroup H A B where + faithful := ⟨fun h ↦ e.injective <| hG.faithful.eq_of_smul_eq_smul <| by simpa only [he]⟩ + commutes := ⟨fun x a b ↦ by simpa [he] using hG.commutes.smul_comm (e x) a b⟩ + isInvariant := ⟨fun b h ↦ + have he' : ∀ (g : G) (x : B), e.symm g • x = g • x := fun g x ↦ by simp [← he] + hG.isInvariant.isInvariant b (fun g ↦ by simpa [he'] using h (e.symm g))⟩ + +variable {G A B} in +theorem iff_of_mulEquiv {H : Type*} [Group H] [MulSemiringAction H B] + (e : H ≃* G) (he : ∀ h (x : B), e h • x = h • x) : + IsGaloisGroup H A B ↔ IsGaloisGroup G A B := by + refine ⟨fun h ↦ h.of_mulEquiv e.symm fun g x ↦ ?_, fun h ↦ h.of_mulEquiv e he⟩ + rw [← he, e.apply_symm_apply] + +variable {G A B} in +@[simp] +theorem top_iff : IsGaloisGroup (⊤ : Subgroup G) A B ↔ IsGaloisGroup G A B := + iff_of_mulEquiv Subgroup.topEquiv fun _ _ ↦ rfl + +instance [IsGaloisGroup G A B] : IsGaloisGroup (⊤ : Subgroup G) A B := + IsGaloisGroup.top_iff.mpr ‹_› + +theorem of_algEquiv [hG : IsGaloisGroup G A B] (B' : Type*) [Semiring B'] + [Algebra A B'] [MulSemiringAction G B'] (e : B ≃ₐ[A] B') + (he : ∀ (g : G) (x : B), e (g • x) = g • (e x)) : + IsGaloisGroup G A B' where + faithful := ⟨fun h ↦ hG.faithful.eq_of_smul_eq_smul fun b ↦ by simpa [← he] using h (e b)⟩ + commutes := ⟨fun g a b' ↦ by + have h' {x'} : e.symm (g • x') = g • e.symm x' := by + apply e.injective + simp [he] + apply e.symm.injective + simpa [h', map_smul] using hG.commutes.smul_comm g a (e.symm b')⟩ + isInvariant := ⟨fun x' hx' ↦ by + obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant (e.symm x') (fun g ↦ by + apply e.injective + simp [he, hx']) + exact ⟨a, by rw [← e.commutes, ha, AlgEquiv.apply_symm_apply]⟩⟩ + +theorem of_ringHom_surjective [hG : IsGaloisGroup G A B] [CommSemiring A'] + [Algebra A' B] (e : A →+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a) + (he' : Function.Surjective e) : IsGaloisGroup G A' B where + faithful := hG.faithful + commutes := ⟨by + intro g a' b + obtain ⟨a, rfl⟩ : ∃ a, e a = a' := he' a' + rw [Algebra.smul_def, Algebra.smul_def, he, ← Algebra.smul_def, ← Algebra.smul_def] + exact hG.commutes.smul_comm g a b⟩ + isInvariant := ⟨by + intro b h + obtain ⟨a, ha⟩ := hG.isInvariant.isInvariant b h + exact ⟨e a, by rw [he, ha]⟩⟩ + +theorem of_ringEquiv [hG : IsGaloisGroup G A B] [CommSemiring A'] [Algebra A' B] + (e : A ≃+* A') (he : ∀ a, algebraMap A' B (e a) = algebraMap A B a) : + IsGaloisGroup G A' B := + .of_ringHom_surjective G A A' B e he e.surjective + +attribute [instance low] IsGaloisGroup.commutes IsGaloisGroup.isInvariant + +variable [hA : IsGaloisGroup G A B] [FaithfulSMul A B] + +/-- If `B/A` is Galois with Galois group `G`, then `A` is isomorphic to the subring of elements of +`B` fixed by `G`. -/ +@[simps apply_coe] +noncomputable def ringEquivFixedPoints : + A ≃+* FixedPoints.subsemiring B G where + toFun x := ⟨algebraMap A B x, fun _ ↦ by rw [smul_algebraMap]⟩ + invFun x := (hA.isInvariant.isInvariant x x.prop).choose + map_mul' _ _ := by simp [Subtype.ext_iff] + map_add' _ _ := by simp [Subtype.ext_iff] + left_inv _ := by simp + right_inv x := by simpa [Subtype.ext_iff] using (hA.isInvariant.isInvariant x x.prop).choose_spec + +@[simp] +theorem algebraMap_ringEquivFixedPoints_symm_apply (x : FixedPoints.subsemiring B G) : + algebraMap A B ((ringEquivFixedPoints G A B).symm x) = x := + (hA.isInvariant.isInvariant x x.prop).choose_spec + +variable [CommSemiring A'] [Algebra A' B] [FaithfulSMul A' B] [hA' : IsGaloisGroup G A' B] + +/-- If `B/A` and `B/A'` are Galois with the same Galois group, then `A ≃+* A'`. -/ +noncomputable def ringEquiv : A ≃+* A' := + (ringEquivFixedPoints G A B).trans (ringEquivFixedPoints G A' B).symm + +@[simp] +theorem algebraMap_ringEquiv_apply (x : A) : + algebraMap A' B (IsGaloisGroup.ringEquiv G A A' B x) = algebraMap A B x := by + simp [ringEquiv] + +@[simp] +theorem algebraMap_ringEquiv_symm_apply (x : A') : + algebraMap A B ((IsGaloisGroup.ringEquiv G A A' B).symm x) = algebraMap A' B x := by + simp [ringEquiv] + +end IsGaloisGroup diff --git a/Mathlib/RingTheory/IsTensorProduct.lean b/Mathlib/RingTheory/IsTensorProduct.lean index 85f81da838dd3c..bd60b3fdb3bbeb 100644 --- a/Mathlib/RingTheory/IsTensorProduct.lean +++ b/Mathlib/RingTheory/IsTensorProduct.lean @@ -166,13 +166,13 @@ end map section variable {R S : Type*} [CommSemiring R] [CommSemiring S] [Algebra R S] - {M₁ M₂ M₃ M₁₂ M₂₃ : Type*} [AddCommMonoid M₁] [AddCommMonoid M₂] [AddCommMonoid M₃] - [AddCommMonoid M₁₂] [AddCommMonoid M₂₃] - [Module R M₁] - [Module R M₂] [Module S M₂] [IsScalarTower R S M₂] - [Module R M₃] [Module S M₃] [IsScalarTower R S M₃] - [Module R M₁₂] [Module S M₁₂] [IsScalarTower R S M₁₂] - [Module R M₂₃] [Module S M₂₃] [IsScalarTower R S M₂₃] + {M₁ M₂ M₃ M₁₂ M₂₃ : Type*} [AddCommMonoid M₁] [AddCommMonoid M₂] [AddCommMonoid M₃] + [AddCommMonoid M₁₂] [AddCommMonoid M₂₃] + [Module R M₁] + [Module R M₂] [Module S M₂] [IsScalarTower R S M₂] + [Module R M₃] [Module S M₃] [IsScalarTower R S M₃] + [Module R M₁₂] [Module S M₁₂] [IsScalarTower R S M₁₂] + [Module R M₂₃] [Module S M₂₃] [IsScalarTower R S M₂₃] set_option backward.defeqAttrib.useBackward true in /-- (Implementation): Use the more linear `IsTensorProduct.assoc`. -/ diff --git a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean index 1220b5ccd0bf32..3b95f71029bacd 100644 --- a/Mathlib/RingTheory/Kaehler/JacobiZariski.lean +++ b/Mathlib/RingTheory/Kaehler/JacobiZariski.lean @@ -8,26 +8,47 @@ module public import Mathlib.RingTheory.Extension.Cotangent.Basic public import Mathlib.RingTheory.Extension.Generators public import Mathlib.Algebra.Module.SnakeLemma +public import Mathlib.RingTheory.Flat.Basic /-! # The Jacobi-Zariski exact sequence -Given `R → S → T`, the Jacobi-Zariski exact sequence is -``` -H¹(L_{T/R}) → H¹(L_{T/S}) → T ⊗[S] Ω[S/R] → Ω[T/R] → Ω[T/S] → 0 -``` -The maps are +Given algebras $R \to S \to T$, the Jacobi-Zariski exact sequence is a long exact sequence +relating the first homology of the naive cotangent complexes and the Kähler differentials of +the respective algebras. It takes the form: +$$ +H_1(L_{T/R}) \to H_1(L_{T/S}) \to T \otimes_S \Omega_{S/R} \to \Omega_{T/R} \to \Omega_{T/S} \to 0 +$$ +The maps in the sequence are - `Algebra.H1Cotangent.map` - `Algebra.H1Cotangent.δ` - `KaehlerDifferential.mapBaseChange` - `KaehlerDifferential.map` -and the exactness lemmas are +The exactness lemmas are - `Algebra.H1Cotangent.exact_map_δ` - `Algebra.H1Cotangent.exact_δ_mapBaseChange` - `KaehlerDifferential.exact_mapBaseChange_map` - `KaehlerDifferential.map_surjective` + +When $T$ is flat over $S$, the left bottom part of the snake lemma diagram used in +the construction of the connecting homomorphism `Algebra.Generators.H1Cotangent.δ` +naturally extends via a base change map. The exactness lemma is +`Algebra.Generators.H1Cotangent.exact_liftBaseChange_map_of_flat`. Globally, this extends +the Jacobi-Zariski exact sequence to the left via a natural base change map, taking the form +$$ +T \otimes_S H_1(L_{S/R}) \to H_1(L_{T/R}) \to H_1(L_{T/S}) +$$ +The exactness lemma is `Algebra.H1Cotangent.exact_liftBaseChange_map_of_flat`. + +# TODO + +The flatness assumption in `Algebra.H1Cotangent.exact_liftBaseChange_map_of_flat` +is stronger than the `Tor`-vanishing conditions required in the full statement of +[Stacks Project, 00S2], this should be refactored and generalized once more API +for `Tor` modules is available. + -/ @[expose] public section @@ -130,12 +151,6 @@ def CotangentSpace.compEquiv : (Q.comp P).cotangentSpaceBasis.repr.trans (Q.cotangentSpaceBasis.prod (P.cotangentSpaceBasis.baseChange T)).repr.symm -section instanceProblem - --- Note: these instances are needed to prevent instance search timeouts. -attribute [local instance 999999] Zero.toOfNat0 SemilinearMapClass.distribMulActionSemiHomClass - SemilinearEquivClass.instSemilinearMapClass instAddZeroClassTensorProduct AddZero.toZero - lemma CotangentSpace.compEquiv_symm_inr : (compEquiv Q P).symm.toLinearMap ∘ₗ LinearMap.inr T Q.toExtension.CotangentSpace (T ⊗[S] P.toExtension.CotangentSpace) = @@ -446,9 +461,68 @@ lemma exact_map_δ' (f : Hom W Q) : rw [← Extension.H1Cotangent.map_comp, Extension.H1Cotangent.map_eq _ (Q.ofComp P).toExtensionHom] exact exact_map_δ Q P -end H1Cotangent +open LinearMap in +lemma liftBaseChange_range_le : + (liftBaseChange T (Extension.H1Cotangent.map (Q.toComp P).toExtensionHom)).range ≤ + (Extension.H1Cotangent.map (Q.ofComp P).toExtensionHom).ker := by + rw [range_liftBaseChange, coe_range, Submodule.span_le, Set.range_subset_iff] + rintro ⟨x, _⟩ + obtain ⟨⟨(x : P.Ring), x_in⟩, rfl⟩ := Extension.Cotangent.mk_surjective x + ext; suffices (Q.ofComp P).toAlgHom ((Q.toComp P).toAlgHom x) ∈ Q.toExtension.ker ^ 2 by + simpa [Ideal.toCotangent_eq_zero] + rw [← Generators.ker, Generators.ker_eq_ker_aeval_val] at x_in + rw [toComp_toAlgHom, toAlgHom_ofComp_rename, Generators.algebraMap_eq, RingHom.coe_coe, + x_in, RingHom.map_zero] + exact Ideal.zero_mem _ + +private lemma auxMemKer (z : T ⊗[S] P.toExtension.H1Cotangent) : + LinearMap.liftBaseChange T (Extension.Cotangent.map (Q.toComp P).toExtensionHom) + ((LinearMap.lTensor T Extension.h1Cotangentι) z) ∈ + (Q.comp P).toExtension.cotangentComplex.ker := by + induction z with + | zero => simp + | tmul x y => simp [← Extension.CotangentSpace.map_cotangentComplex] + | add x y hx hy => simpa using Submodule.add_mem _ hx hy + +open LinearMap in +/-- When $T$ is flat over $S$, the left bottom part of the snake lemma diagram used in +the construction of the connecting homomorphism `Algebra.Generators.H1Cotangent.δ` +naturally extends via a base change map. -/ +theorem exact_liftBaseChange_map_of_flat [Module.Flat S T] : + Function.Exact ((Extension.H1Cotangent.map (toComp Q P).toExtensionHom).liftBaseChange T) + (Extension.H1Cotangent.map (ofComp Q P).toExtensionHom) := by + rw [exact_iff] + refine le_antisymm ?_ (liftBaseChange_range_le Q P) + rintro ⟨x, x_in⟩ hx + replace hx : Extension.Cotangent.map (Q.ofComp P).toExtensionHom x = 0 := by + simpa [← Extension.h1Cotangentι_injective.eq_iff] using hx + rw [← mem_ker, (Cotangent.exact Q P).linearMap_ker_eq] at hx + rcases hx with ⟨x, rfl⟩ + rw [mem_ker, ← comp_apply, ← map_comp_cotangentComplex_baseChange, comp_apply, + ← mem_ker, ker_eq_bot.mpr (CotangentSpace.map_toComp_injective Q P), Submodule.mem_bot, + baseChange_eq_ltensor, ← mem_ker, (Module.Flat.lTensor_exact T + P.toExtension.exact_hCotangentι_cotangentComplex).linearMap_ker_eq] at x_in + rcases x_in with ⟨x, rfl⟩ + use x; induction x with + | zero => ext; simp + | tmul x y => ext; simp + | add x y hx hy => ext; simp [hx (auxMemKer Q P x), hy (auxMemKer Q P y)] + +/-- A variant of `exact_liftBaseChange_map_of_flat` that takes in +arbitrary maps between generators. -/ +theorem exact_liftBaseChange_map_of_flat' [Module.Flat S T] (f : Hom W Q) (g : Hom P W) : + Function.Exact ((Extension.H1Cotangent.map g.toExtensionHom).liftBaseChange T) + (Extension.H1Cotangent.map f.toExtensionHom) := by + rw [← LinearEquiv.conj_exact_iff_exact _ _ (H1Cotangent.equiv W (Q.comp P))] + convert! exact_liftBaseChange_map_of_flat Q P + · change Extension.H1Cotangent.map (W.defaultHom (Q.comp P)).toExtensionHom ∘ₗ _ = _ + rw [LinearMap.liftBaseChange_comp, ← Extension.H1Cotangent.map_comp, + Extension.H1Cotangent.map_eq] + · change (Extension.H1Cotangent.map f.toExtensionHom).restrictScalars T ∘ₗ + (Extension.H1Cotangent.map _) = _ + rw [← Extension.H1Cotangent.map_comp, Extension.H1Cotangent.map_eq] -end instanceProblem +end H1Cotangent end Generators @@ -461,13 +535,29 @@ noncomputable def H1Cotangent.δ : H1Cotangent S T →ₗ[T] T ⊗[S] Ω[S⁄R] := Generators.H1Cotangent.δ (Generators.self S T) (Generators.self R S) -/-- Given algebras `R → S → T`, `H¹(L_{T/R}) → H¹(L_{T/S}) → T ⊗[S] Ω[S/R]` is exact. -/ +/-- Given algebras $R \to S \to T$, the sequence +$H_1(L_{T/R}) \to H_1(L_{T/S}) \to T \otimes_S \Omega_{S/R}$ +is exact. -/ +@[stacks 00S2] lemma H1Cotangent.exact_map_δ : Function.Exact (map R S T T) (δ R S T) := Generators.H1Cotangent.exact_map_δ' (Generators.self S T) (Generators.self R S) (Generators.self R T) (Generators.defaultHom _ _) -/-- Given algebras `R → S → T`, `H¹(L_{T/S}) → T ⊗[S] Ω[S/R] → Ω[T/R]` is exact. -/ +/-- Given algebras $R \to S \to T$, the sequence +$H_1(L_{T/S}) \to T \otimes_S \Omega_{S/R} \to \Omega_{T/R}$ +is exact. -/ +@[stacks 00S2] lemma H1Cotangent.exact_δ_mapBaseChange : Function.Exact (δ R S T) (mapBaseChange R S T) := Generators.H1Cotangent.exact_δ_map (Generators.self S T) (Generators.self R S) +/-- Given algebras $R \to S \to T$ and $T$ flat over $S$, the sequence +$T \otimes_S H_1(L_{S/R}) \to H_1(L_{T/R}) \to H_1(L_{T/S})$ +is exact. -/ +@[stacks 00S2] +lemma H1Cotangent.exact_liftBaseChange_map_of_flat [Module.Flat S T] : + Function.Exact ((map R R S T).liftBaseChange T) (map R S T T) := + Generators.H1Cotangent.exact_liftBaseChange_map_of_flat' + (Generators.self S T) (Generators.self R S) (Generators.self R T) + (Generators.defaultHom _ _) (Generators.defaultHom _ _) + end Algebra diff --git a/Mathlib/RingTheory/LocalRing/Defs.lean b/Mathlib/RingTheory/LocalRing/Defs.lean index 211784be77b0d3..9538400b1f4589 100644 --- a/Mathlib/RingTheory/LocalRing/Defs.lean +++ b/Mathlib/RingTheory/LocalRing/Defs.lean @@ -12,14 +12,14 @@ public import Mathlib.Algebra.Ring.Defs # Local rings -Define local rings as commutative rings having a unique maximal ideal. +Define the notion of a local ring for non-commutative semirings. In the commutative case, +this is shown to be equivalent to the familiar definition that there exists a unique +maximal ideal in `IsLocalRing.of_unique_max_ideal` and `IsLocalRing.maximal_ideal_unique`. ## Main definitions * `IsLocalRing`: A predicate on semirings, stating that for any pair of elements that - adds up to `1`, one of them is a unit. In the commutative case this is shown to be equivalent - to the condition that there exists a unique maximal ideal, see - `IsLocalRing.of_unique_max_ideal` and `IsLocalRing.maximal_ideal_unique`. + adds up to `1`, one of them is a unit. -/ diff --git a/Mathlib/RingTheory/LocalRing/Pullback.lean b/Mathlib/RingTheory/LocalRing/Pullback.lean new file mode 100644 index 00000000000000..544f6634a4a90c --- /dev/null +++ b/Mathlib/RingTheory/LocalRing/Pullback.lean @@ -0,0 +1,153 @@ +/- +Copyright (c) 2026 Bingyu Xia. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Bingyu Xia +-/ + +module + +public import Mathlib.Algebra.Torsor.Defs +public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Basic + +import Mathlib.Algebra.Ring.Subring.Units +import Mathlib.RingTheory.LocalRing.RingHom.Basic + +/-! +# Local Ring Properties of Equalizers and Pullbacks + +In this file we provide basic lemmas for the equalizers the pullbacks and of ring homomorphisms +and algebra homomorphisms. We show that they preserve the property of being a local ring under +suitable conditions. + +## Main definitions + +* `RingHom.pullback`: The pullback of two ring homomorphisms `f : R →+* T` and `g : S →+* T`, + defined as the subring of `R × S` consisting of pairs `(r, s)` such that `f r = g s`. + +* `RingHom.pullbackFst`, `RingHom.pullbackSnd`: The canonical projection maps from the + pullback to `R` and `S`. + +## Main results + +* `RingHom.isLocalRing_eqLocus`: The equalizer of two ring homomorphisms from a local + ring is again a local ring. + +* `RingHom.isLocalRing_pullback`: The pullback of `f : R →+* T` and `g : S →+* T` is a + local ring, provided that `R` is a local ring and `g` is a local homomorphism. + +-/ + +@[expose] public section + +namespace RingHom + +variable {R S T : Type*} [Ring R] [Ring S] [Semiring T] + +theorem isLocalRing_eqLocus [IsLocalRing R] (f g : R →+* T) : IsLocalRing (f.eqLocus g) := + (f.eqLocus g).subtype.domain_isLocalRing + +/-- The subring of pairs `(r, s) : R × S` such that `f r = g s`, i.e., + the pullback of `f : R →+* T` and `g : S →+* T` as a subring of `R × S`. -/ +abbrev pullback (f : R →+* T) (g : S →+* T) : Subring (R × S) := + (f.comp (RingHom.fst R S)).eqLocus <| g.comp (RingHom.snd R S) + +/-- The first projection from the pullback of `f : R →+* T` and `g : S →+* T` to `R`. -/ +abbrev pullbackFst (f : R →+* T) (g : S →+* T) : f.pullback g →+* R := + (RingHom.fst R S).comp (RingHom.pullback f g).subtype + +/-- The second projection from the pullback of `f : R →+* T` and `g : S →+* T` to `S`. -/ +abbrev pullbackSnd (f : R →+* T) (g : S →+* T) : f.pullback g →+* S := + (RingHom.snd R S).comp (f.pullback g).subtype + +theorem pullback_comm_sq (f : R →+* T) (g : S →+* T) : + f.comp (f.pullbackFst g) = g.comp (f.pullbackSnd g) := + ext fun x ↦ x.prop + +theorem isUnit_pullback_mk_iff (f : R →+* T) (g : S →+* T) {a : R × S} (a_in : a ∈ f.pullback g) : + IsUnit (⟨a, a_in⟩ : f.pullback g) ↔ IsUnit a.1 ∧ IsUnit a.2 := by + rw [isUnit_eqLocus_mk_iff, Prod.isUnit_iff] + +instance isLocalHom_pullbackFst (f : R →+* T) (g : S →+* T) [IsLocalHom g] : + IsLocalHom (f.pullbackFst g) where + map_nonunit := fun ⟨⟨_, _⟩, h_in⟩ ha ↦ + (isUnit_pullback_mk_iff f g h_in).mpr ⟨ha, isUnit_of_map_unit g _ (h_in ▸ ha.map f)⟩ + +instance isLocalHom_pullbackSnd (f : R →+* T) (g : S →+* T) [IsLocalHom f] : + IsLocalHom (f.pullbackSnd g) where + map_nonunit := fun ⟨⟨_, _⟩, h_in⟩ ha ↦ + (isUnit_pullback_mk_iff f g h_in).mpr ⟨isUnit_of_map_unit f _ (h_in.symm ▸ ha.map g), ha⟩ + +theorem surjective_pullbackFst_of_surjective (f : R →+* T) (g : S →+* T) + (h : Function.Surjective g) : Function.Surjective (f.pullbackFst g) := + fun r ↦ by simpa [eq_comm] using h (f r) + +theorem surjective_pullbackSnd_of_surjective (f : R →+* T) (g : S →+* T) + (h : Function.Surjective f) : Function.Surjective (f.pullbackSnd g) := + fun s ↦ by simpa [eq_comm] using h (g s) + +theorem map_pullbackSnd_ker_pullbackFst_eq (f : R →+* T) (g : S →+* T) : + Ideal.map (f.pullbackSnd g) (RingHom.ker (f.pullbackFst g)) = RingHom.ker g := by + apply le_antisymm + · rw [Ideal.map_le_iff_le_comap] + rintro ⟨⟨_, _⟩, h⟩ + simp at h ⊢; grind + · intro s hs + exact Ideal.mem_map_of_mem (f.pullbackSnd g) (x := ⟨(0, s), by simpa using hs.symm⟩) + (I := RingHom.ker (f.pullbackFst g)) (by simp) + +theorem isLocalRing_pullback [IsLocalRing R] (f : R →+* T) (g : S →+* T) [IsLocalHom g] : + IsLocalRing (f.pullback g) := (f.pullbackFst g).domain_isLocalRing + +end RingHom + +namespace AlgHom + +variable {R A B C : Type*} [CommSemiring R] + +section Semiring + +variable [Semiring A] [Algebra R A] [Semiring B] [Algebra R B] [Semiring C] [Algebra R C] + +/-- The subalgebra of pairs `(a, b) : A × B` such that `f a = g b`, i.e., + the pullback of f and g as a subalgebra of A × B. -/ +abbrev pullback (f : A →ₐ[R] C) (g : B →ₐ[R] C) : Subalgebra R (A × B) := equalizer + (f.comp (fst R A B)) (g.comp (snd R A B)) + +/-- The first projection from the pullback of `f` and `g` to `A`. -/ +abbrev pullbackFst (f : A →ₐ[R] C) (g : B →ₐ[R] C) : pullback f g →ₐ[R] A := + (fst R A B).comp (pullback f g).val + +/-- The second projection from the pullback of `f` and `g` to `B`. -/ +abbrev pullbackSnd (f : A →ₐ[R] C) (g : B →ₐ[R] C) : pullback f g →ₐ[R] B := + (snd R A B).comp (pullback f g).val + +theorem pullback_comm_sq (f : A →ₐ[R] C) (g : B →ₐ[R] C) : + f.comp (pullbackFst f g) = g.comp (pullbackSnd f g) := + AlgHom.ext fun x ↦ x.prop + +end Semiring + +section Ring + +variable [Ring A] [Algebra R A] [Ring B] [Algebra R B] [Semiring C] [Algebra R C] + +theorem isUnit_pullback_mk_iff (f : A →ₐ[R] C) (g : B →ₐ[R] C) {a : A × B} + (a_in : a ∈ f.pullback g) : IsUnit (⟨a, a_in⟩ : f.pullback g) ↔ + IsUnit a.1 ∧ IsUnit a.2 := + RingHom.isUnit_pullback_mk_iff (f : A →+* C) (g : B →+* C) a_in + +theorem surjective_pullbackFst_of_surjective (f : A →ₐ[R] C) (g : B →ₐ[R] C) + (h : Function.Surjective g) : Function.Surjective (pullbackFst f g) := + RingHom.surjective_pullbackFst_of_surjective (f : A →+* C) (g : B →+* C) h + +theorem surjective_pullbackSnd_of_surjective (f : A →ₐ[R] C) (g : B →ₐ[R] C) + (h : Function.Surjective f) : Function.Surjective (pullbackSnd f g) := + RingHom.surjective_pullbackSnd_of_surjective (f : A →+* C) (g : B →+* C) h + +theorem isLocalRing_pullback [IsLocalRing A] (f : A →ₐ[R] C) (g : B →ₐ[R] C) + [IsLocalHom g] : IsLocalRing (f.pullback g) := + RingHom.isLocalRing_pullback (f : A →+* C) (g : B →+* C) + +end Ring + +end AlgHom diff --git a/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean b/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean index 5841aa7aa67bef..dd141c6c82a710 100644 --- a/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean +++ b/Mathlib/RingTheory/LocalRing/ResidueField/Defs.lean @@ -7,6 +7,7 @@ module public import Mathlib.RingTheory.Ideal.Quotient.Basic public import Mathlib.RingTheory.LocalRing.MaximalIdeal.Basic +public import Mathlib.Tactic.CrossRefAttribute /-! @@ -25,6 +26,7 @@ namespace IsLocalRing variable (R : Type*) [CommRing R] [IsLocalRing R] /-- The residue field of a local ring is the quotient of the ring by its maximal ideal. -/ +@[wikidata Q7315530] def ResidueField := R ⧸ maximalIdeal R deriving CommRing, Inhabited diff --git a/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean b/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean index f6d7e2c2ee8dfd..e76b5c7af8ce7c 100644 --- a/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean +++ b/Mathlib/RingTheory/LocalRing/ResidueField/Ideal.lean @@ -246,3 +246,6 @@ lemma Ideal.ResidueField.ringHom_ext {I : Ideal R} [I.IsPrime] lemma Ideal.ResidueField.algHom_ext {I : Ideal A} [I.IsPrime] {f g : I.ResidueField →ₐ[R] B} (H : f.comp (IsScalarTower.toAlgHom R A _) = g.comp (IsScalarTower.toAlgHom R A _)) : f = g := AlgHom.coe_ringHom_injective (ringHom_ext congr($H)) + +@[simp] lemma Ideal.ResidueField.mapₐ_id (I : Ideal A) [I.IsPrime] : + Ideal.ResidueField.mapₐ I I (.id R A) rfl = .id _ _ := by ext; simp diff --git a/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean b/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean index 1ab85fd57885fd..deeaae7fdb0531 100644 --- a/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean +++ b/Mathlib/RingTheory/LocalRing/RingHom/Basic.lean @@ -45,13 +45,12 @@ theorem isLocalHom_of_comp (f : R →+* S) (g : S →+* T) [IsLocalHom (g.comp f ⟨fun _ ha => (isUnit_map_iff (g.comp f) _).mp (g.isUnit_map ha)⟩ /-- If `f : R →+* S` is a local ring hom, then `R` is a local ring if `S` is. -/ -theorem RingHom.domain_isLocalRing {R S : Type*} [Semiring R] [CommSemiring S] [IsLocalRing S] - (f : R →+* S) [IsLocalHom f] : IsLocalRing R := by - haveI : Nontrivial R := f.domain_nontrivial - apply IsLocalRing.of_nonunits_add - intro a b - simp_rw [← map_mem_nonunits_iff f, f.map_add] - exact IsLocalRing.nonunits_add +theorem RingHom.domain_isLocalRing [IsLocalRing S] (f : R →+* S) [IsLocalHom f] : + IsLocalRing R where + toNontrivial := f.domain_nontrivial + isUnit_or_isUnit_of_add_one {a b} h := Or.imp + (isUnit_of_map_unit f a) (isUnit_of_map_unit f b) + (IsLocalRing.isUnit_or_isUnit_of_add_one (by rw [← map_add, h, map_one])) end diff --git a/Mathlib/RingTheory/Localization/AtPrime/Extension.lean b/Mathlib/RingTheory/Localization/AtPrime/Extension.lean index e75652d57a4887..7a87b43cdb7204 100644 --- a/Mathlib/RingTheory/Localization/AtPrime/Extension.lean +++ b/Mathlib/RingTheory/Localization/AtPrime/Extension.lean @@ -5,7 +5,7 @@ Authors: Xavier Roblot -/ module -public import Mathlib.NumberTheory.RamificationInertia.Basic +public import Mathlib.RingTheory.RamificationInertia.Basic /-! # Primes in an extension of localization at prime @@ -175,32 +175,28 @@ theorem inertiaDeg_map_eq_inertiaDeg [p.IsMaximal] [P.IsMaximal] ext x exact algebraMap_equivQuotMaximalIdeal_symm_apply p Rₚ Sₚ P x -theorem ramificationIdx_map_eq_ramificationIdx [IsDomain R] [IsTorsionFree R S] [IsTorsionFree R Rₚ] - [IsTorsionFree S Sₚ] [IsTorsionFree Rₚ Sₚ] [IsDedekindDomain S] [IsDedekindDomain Rₚ] - [IsDedekindDomain Sₚ] (hp : p ≠ ⊥) [P.IsPrime] : - (maximalIdeal Rₚ).ramificationIdx (P.map (algebraMap S Sₚ)) = - p.ramificationIdx P := by - have h₁ : maximalIdeal Rₚ ≠ ⊥ := by - rw [← map_eq_maximalIdeal p] - exact map_ne_bot_of_ne_bot hp - have : (P.map (algebraMap S Sₚ)).IsPrime := isPrime_map_of_liesOver S p Sₚ P - by_cases hP : P = ⊥ - · simp_rw [hP, Ideal.map_bot, ramificationIdx_bot' hp - (FaithfulSMul.algebraMap_injective _ _), - ramificationIdx_bot' h₁ (FaithfulSMul.algebraMap_injective Rₚ Sₚ)] - have : P.IsMaximal := IsPrime.isMaximal inferInstance hP - have : (Ideal.map (algebraMap S Sₚ) P).LiesOver (maximalIdeal Rₚ) := - liesOver_map_of_liesOver p Rₚ Sₚ P - have : (Ideal.map (algebraMap S Sₚ) P).LiesOver P := by - rw [liesOver_iff, under_def, comap_map_eq_self_of_isMaximal _ (IsPrime.ne_top')] - have h_main := - (ramificationIdx_algebra_tower' p (maximalIdeal Rₚ) (Ideal.map (algebraMap S Sₚ) P)).symm.trans - <| ramificationIdx_algebra_tower' p P (Ideal.map (algebraMap S Sₚ) P) - rwa [ramificationIdx_map_self_eq_one IsPrime.ne_top' (map_ne_bot_of_ne_bot hP), mul_one, - ← map_eq_maximalIdeal p, ramificationIdx_map_self_eq_one _ (map_ne_bot_of_ne_bot hp), one_mul, - map_eq_maximalIdeal p] at h_main - rw [map_eq_maximalIdeal] - exact IsPrime.ne_top' +include p in +theorem ramificationIdx_map_eq_ramificationIdx [P.IsPrime] : + (P.map (algebraMap S Sₚ)).ramificationIdx Rₚ = P.ramificationIdx R := by + have := liesOver_map_of_liesOver p Rₚ Sₚ P + have := IsLocalization.liesOver_map_of_isPrime_disjoint (algebraMapSubmonoid S p.primeCompl) Sₚ + (Set.disjoint_image_left.mpr (Set.disjoint_compl_left_iff_subset.mpr hPp.over.ge)) + have := isPrime_map_of_liesOver S p Sₚ P + rw [ramificationIdx_eq (maximalIdeal Rₚ) (P.map (algebraMap S Sₚ)), ramificationIdx_eq p P] + let R₁ := Localization.AtPrime (P.map (algebraMap S Sₚ)) + let R₂ := Localization.AtPrime P + let : Algebra R₂ R₁ := Localization.AtPrime.algebraOfLiesOver P (P.map (algebraMap S Sₚ)) + have : IsLocalization.AtPrime R₁ P := by + convert isLocalization_isLocalization_atPrime_isLocalization + (algebraMapSubmonoid S p.primeCompl) R₁ (P.map (algebraMap S Sₚ)) + rw [← Ideal.under_def, ← Ideal.over_def (P.map (algebraMap S Sₚ)) P] + have h : Function.Bijective (algebraMap R₂ R₁) := + (Localization.algEquiv P.primeCompl R₁).bijective + have key : p.map (algebraMap R R₂) = + ((maximalIdeal Rₚ).map (algebraMap Rₚ R₁)).comap (algebraMap R₂ R₁) := by + rw [← IsLocalization.AtPrime.map_eq_maximalIdeal p, p.map_map, ← IsScalarTower.algebraMap_eq, + IsScalarTower.algebraMap_eq R R₂ R₁, ← p.map_map, comap_map_of_bijective _ h] + rw [Module.length_quotient, Module.length_quotient, key, coheight_comap_of_surjective _ h.2] end IsLocalization.AtPrime @@ -250,12 +246,9 @@ theorem primesOverEquivPrimesOver_inertiagDeg_eq [p.IsMaximal] (hp : p ≠ ⊥) have : (P.1.map (algebraMap S Sₚ)).LiesOver (maximalIdeal Rₚ) := liesOver_map_of_liesOver p _ _ _ exact inertiaDeg_map_eq_inertiaDeg p _ _ _ -theorem primesOverEquivPrimesOver_ramificationIdx_eq (hp : p ≠ ⊥) [NoZeroSMulDivisors R Rₚ] - [NoZeroSMulDivisors S Sₚ] [NoZeroSMulDivisors Rₚ Sₚ] [IsDedekindDomain Rₚ] [IsDedekindDomain Sₚ] - (P : p.primesOver S) : - (maximalIdeal Rₚ).ramificationIdx - (primesOverEquivPrimesOver p Rₚ Sₚ hp P : Ideal Sₚ) = - p.ramificationIdx P.val := - ramificationIdx_map_eq_ramificationIdx p _ _ _ hp +theorem primesOverEquivPrimesOver_ramificationIdx_eq (hp : p ≠ ⊥) (P : p.primesOver S) : + (primesOverEquivPrimesOver p Rₚ Sₚ hp P : Ideal Sₚ).ramificationIdx Rₚ = + P.val.ramificationIdx R := + ramificationIdx_map_eq_ramificationIdx p _ _ _ end IsDedekindDomain diff --git a/Mathlib/RingTheory/Localization/Away/Basic.lean b/Mathlib/RingTheory/Localization/Away/Basic.lean index e6c6f68d598a64..2a0c932cff60c2 100644 --- a/Mathlib/RingTheory/Localization/Away/Basic.lean +++ b/Mathlib/RingTheory/Localization/Away/Basic.lean @@ -5,8 +5,8 @@ Authors: Kenny Lau, Mario Carneiro, Johan Commelin, Amelia Livingston, Anne Baan -/ module -public import Mathlib.GroupTheory.MonoidLocalization.Away public import Mathlib.Algebra.Algebra.Pi +public import Mathlib.GroupTheory.MonoidLocalization.Away public import Mathlib.RingTheory.Ideal.Maps public import Mathlib.RingTheory.Localization.Basic public import Mathlib.RingTheory.UniqueFactorizationDomain.Multiplicity @@ -310,6 +310,10 @@ lemma commutes {R : Type*} [CommSemiring R] (S₁ S₂ T : Type*) [CommSemiring ext x simp +theorem isDomain [IsDomain R] {x : R} (hx : x ≠ 0) [IsLocalization.Away x S] : IsDomain S := + IsLocalization.isDomain_of_le_nonZeroDivisors S + (powers_le_nonZeroDivisors_of_noZeroDivisors hx) + end Away end Away @@ -590,6 +594,10 @@ theorem existsUnique_algebraMap_eq_of_span_eq_top (s : Set R) (span_eq : Ideal.s simp_rw [← map_pow, eq, ← map_mul, Finset.sum_mul, mul_assoc, eq2 _ a, mul_left_comm (c _), ← Finset.mul_sum, ← smul_eq_mul (a := c _), eq1, mul_one] +/-- If `x ≠ 0`, then the localization of a domain away from `x` is again a domain. -/ +theorem Away.isDomain [IsDomain R] {x : R} (hx : x ≠ 0) : IsDomain (Localization.Away x) := + IsLocalization.Away.isDomain (Localization.Away x) hx + end Localization end CommSemiring diff --git a/Mathlib/RingTheory/Localization/Away/Lemmas.lean b/Mathlib/RingTheory/Localization/Away/Lemmas.lean index 70a7c32959e6a2..36c6b0d1ac3b95 100644 --- a/Mathlib/RingTheory/Localization/Away/Lemmas.lean +++ b/Mathlib/RingTheory/Localization/Away/Lemmas.lean @@ -68,3 +68,27 @@ lemma quotient_of_isIdempotentElem {e : R} (he : IsIdempotentElem e) : away_of_isIdempotentElem he Ideal.mk_ker Quotient.mk_surjective end IsLocalization.Away + +section saturated + +variable {R : Type*} (S : Type*) [CommSemiring R] [CommSemiring S] + [Algebra R S] (x : R) [IsLocalization.Away x S] {I J : Ideal R} + +lemma Ideal.le_of_map_algebraMap_le (hle : I.map (algebraMap R S) ≤ J.map (algebraMap R S)) + (hxJ : ∀ y : R, x * y ∈ J → y ∈ J) : I ≤ J := by + intro y hy + have hin : algebraMap R S y ∈ I.map (algebraMap R S) := Ideal.mem_map_of_mem (algebraMap R S) hy + grw [hle, IsLocalization.algebraMap_mem_map_algebraMap_iff (Submonoid.powers x)] at hin + obtain ⟨m, ⟨n, hn, rfl⟩, h⟩ := hin + dsimp at h + induction n with + | zero => simpa using h + | succ n ih => + rw [add_comm, pow_add, pow_one, mul_assoc] at h + exact ih <| hxJ _ h + +lemma Ideal.eq_of_map_algebraMap_le (heq : I.map (algebraMap R S) = J.map (algebraMap R S)) + (hxI : ∀ y : R, x * y ∈ I → y ∈ I) (hxJ : ∀ y : R, x * y ∈ J → y ∈ J) : I = J := + le_antisymm (le_of_map_algebraMap_le S x heq.le hxJ) (le_of_map_algebraMap_le S x heq.ge hxI) + +end saturated diff --git a/Mathlib/RingTheory/Localization/FractionRing.lean b/Mathlib/RingTheory/Localization/FractionRing.lean index 00659e147c0d83..54d432f40eba71 100644 --- a/Mathlib/RingTheory/Localization/FractionRing.lean +++ b/Mathlib/RingTheory/Localization/FractionRing.lean @@ -437,6 +437,10 @@ lemma ringEquivOfRingEquiv_algebraMap (a : A) : ringEquivOfRingEquiv h (algebraMap A K a) = algebraMap B L (h a) := by simp +@[simp] +lemma ringEquivOfRingEquiv_refl : + ringEquivOfRingEquiv (.refl A) = .refl K := by ext; simp + @[simp] lemma ringEquivOfRingEquiv_symm : (ringEquivOfRingEquiv h : K ≃+* L).symm = ringEquivOfRingEquiv h.symm := rfl @@ -449,6 +453,26 @@ theorem ringEquivOfRingEquiv_comp {C : Type*} (M : Type*) [CommRing C] ext a simp [IsLocalization.map_map] +variable (A K) + +/-- A ring automorphism of a ring induces an ring automorphism of its fraction field. + +This is a bundled version of `ringEquivOfRingEquiv`. -/ +noncomputable def ringEquivOfRingEquivHom : (A ≃+* A) →* (K ≃+* K) where + toFun := ringEquivOfRingEquiv + map_one' := ringEquivOfRingEquiv_refl + map_mul' f g := ringEquivOfRingEquiv_comp K K K g f + +@[simp] +lemma ringEquivOfRingEquivHom_apply (f : A ≃+* A) : + ringEquivOfRingEquivHom A K f = ringEquivOfRingEquiv f := + rfl + +lemma ringEquivOfRingEquivHom_injective : Function.Injective (ringEquivOfRingEquivHom A K) := by + intro f g h + ext b + simpa using RingEquiv.ext_iff.mp h (algebraMap A K b) + end ringEquivOfRingEquiv section semilinearEquivOfRingEquiv @@ -624,21 +648,21 @@ variable (G A B K L : Type*) [Group G] [CommRing A] [CommRing B] [MulSemiringAct /-- Given a `MulSemiringAction G B`, extend the action of `G` on `B` to a `MulSemiringAction G L` on the fraction field `L` of `B`. -/ @[implicit_reducible] -noncomputable def mulSemiringAction [SMulCommClass G A B] : +noncomputable def mulSemiringAction : MulSemiringAction G L := MulSemiringAction.compHom L - ((fieldEquivOfAlgEquivHom K L).comp (MulSemiringAction.toAlgAut G A B)) + ((ringEquivOfRingEquivHom B L).comp (MulSemiringAction.toRingEquiv G B)) /-- The action of `G` on the fraction field `L` of `B` given by `IsFractionRing.mulSemiringAction` is compatible with the embedding `B ⊆ L`. -/ -instance smulDistribClass [SMulCommClass G A B] : - letI := mulSemiringAction G A B K L +instance smulDistribClass : + letI := mulSemiringAction G B L SMulDistribClass G B L := - let := mulSemiringAction G A B K L + let := mulSemiringAction G B L ⟨fun g b x ↦ by rw [Algebra.smul_def', Algebra.smul_def', smul_mul'] congr - apply fieldEquivOfAlgEquiv_algebraMap⟩ + apply ringEquivOfRingEquiv_algebraMap⟩ variable [MulSemiringAction G L] [SMulDistribClass G B L] diff --git a/Mathlib/RingTheory/Localization/Ideal.lean b/Mathlib/RingTheory/Localization/Ideal.lean index f2cb0211365cf5..35ae3cd89208ca 100644 --- a/Mathlib/RingTheory/Localization/Ideal.lean +++ b/Mathlib/RingTheory/Localization/Ideal.lean @@ -169,6 +169,10 @@ theorem under_map_of_isPrime_disjoint {I : Ideal R} (hI : I.IsPrime) (hM : Disjo @[deprecated (since := "2026-04-09")] alias comap_map_of_isPrime_disjoint := under_map_of_isPrime_disjoint +theorem liesOver_map_of_isPrime_disjoint {I : Ideal R} [I.IsPrime] (hM : Disjoint (M : Set R) I) : + (I.map (algebraMap R S)).LiesOver I := + ⟨(under_map_of_isPrime_disjoint M S ‹_› hM).symm⟩ + /-- If `S` is the localization of `R` at a submonoid, the ordering of ideals of `S` is embedded in the ordering of ideals of `R`. -/ def orderEmbedding : Ideal S ↪o Ideal R where diff --git a/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean b/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean index a20511d3dcb445..512daafeff3a99 100644 --- a/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean +++ b/Mathlib/RingTheory/MvPolynomial/Symmetric/FundamentalTheorem.lean @@ -142,7 +142,7 @@ lemma esymmAlgHom_apply (p : MvPolynomial (Fin n) R) : lemma rename_esymmAlgHom (e : σ ≃ τ) : (renameSymmetricSubalgebra e).toAlgHom.comp (esymmAlgHom σ R n) = esymmAlgHom τ R n := by ext i : 2 - simp_rw [AlgHom.comp_apply, esymmAlgHom, aeval_X, AlgEquiv.coe_algHom, + simp_rw [AlgHom.comp_apply, esymmAlgHom, aeval_X, AlgEquiv.coe_toAlgHom, renameSymmetricSubalgebra_apply_coe, rename_esymm] variable (σ) in diff --git a/Mathlib/RingTheory/MvPowerSeries/Basic.lean b/Mathlib/RingTheory/MvPowerSeries/Basic.lean index 741d71157d56c1..08e0614e76f1b0 100644 --- a/Mathlib/RingTheory/MvPowerSeries/Basic.lean +++ b/Mathlib/RingTheory/MvPowerSeries/Basic.lean @@ -234,8 +234,8 @@ theorem coeff_monomial_mul (a : R) : ∀ p ∈ antidiagonal m, coeff (p : (σ →₀ ℕ) × (σ →₀ ℕ)).1 (monomial n a) * coeff p.2 φ ≠ 0 → p.1 = n := fun p _ hp => eq_of_coeff_monomial_ne_zero (left_ne_zero_of_mul hp) - rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.filter_fst_eq_antidiagonal _ n, - Finset.sum_ite_index] + rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.HasAntidiagonal.filter_fst_eq_antidiagonal + _ n, Finset.sum_ite_index] simp only [Finset.sum_singleton, coeff_monomial_same, Finset.sum_empty] theorem coeff_mul_monomial (a : R) : @@ -245,8 +245,8 @@ theorem coeff_mul_monomial (a : R) : ∀ p ∈ antidiagonal m, coeff (p : (σ →₀ ℕ) × (σ →₀ ℕ)).1 φ * coeff p.2 (monomial n a) ≠ 0 → p.2 = n := fun p _ hp => eq_of_coeff_monomial_ne_zero (right_ne_zero_of_mul hp) - rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.filter_snd_eq_antidiagonal _ n, - Finset.sum_ite_index] + rw [coeff_mul, ← Finset.sum_filter_of_ne this, Finset.HasAntidiagonal.filter_snd_eq_antidiagonal + _ n, Finset.sum_ite_index] simp only [Finset.sum_singleton, coeff_monomial_same, Finset.sum_empty] theorem coeff_add_monomial_mul (a : R) : @@ -351,6 +351,15 @@ theorem coeff_C [DecidableEq σ] (n : σ →₀ ℕ) (a : R) : theorem coeff_zero_C (a : R) : coeff (0 : σ →₀ ℕ) (C a) = a := coeff_monomial_same 0 a +theorem coeff_C_of_ne_zero {n : σ →₀ ℕ} (h : n ≠ 0) (a : R) : coeff n (C a) = 0 := by + classical rw [coeff_C, if_neg h] + +-- The intended use case of this theorem is for `m = 1` (often useful for `pderiv`). +@[simp] +theorem coeff_add_single_C {m : ℕ} [NeZero m] {n : σ →₀ ℕ} (a : R) (i : σ) : + coeff (n + single i m) (C a) = 0 := + coeff_C_of_ne_zero (fun H ↦ by simpa [NeZero.ne] using congr($(H) i)) a + @[grind inj] theorem C_injective : Function.Injective (C : R → MvPowerSeries σ R) := by intro a b h diff --git a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean index db2ea3a8a78c2e..37d55774ebae66 100644 --- a/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean +++ b/Mathlib/RingTheory/MvPowerSeries/GaussNorm.lean @@ -33,6 +33,13 @@ the set of all values of `v (coeff t f) * ∏ i : t.support, c i` for all `t : * `MvPowerSeries.gaussNorm_add_le_max`: if `v` is a non-negative non-archimedean function and the set of values `v (coeff t f) * ∏ i : t.support, c i` is bounded above (similarly for `g`), then the Gauss norm has the non-archimedean property. + +* `MvPowerSeries.AchievesGaussNorm`: a type `i` is said to achieve gauss norm if + `v (coeff i f) * i.prod (c · ^ ·) = gaussNorm v c f`. + +* `MvPowerSeries.gaussNorm_neg`: if `v` has the property that `∀ i, v i = v (-i)` then + `gaussNorm v c (-f) = gaussNorm v c f `. + -/ @[expose] public section @@ -164,6 +171,12 @@ variable [Ring R] abbrev AchievesGaussNorm (i : σ →₀ ℕ) : Prop := v (coeff i f) * i.prod (c · ^ ·) = gaussNorm v c f +lemma gaussNorm_neg (vNeg : ∀ x, v (-x) = v x) (f : MvPowerSeries σ R) : + gaussNorm v c (-f) = gaussNorm v c f := by + simp_rw [gaussNorm] + have (t : σ →₀ ℕ) : (coeff t) (-f) = - (coeff t) f := by rfl + simp_rw [this, vNeg] + section absoluteValue variable {α S : Type*} [LinearOrder S] [AddCommGroup α] (f : α → S) @@ -180,7 +193,7 @@ lemma ultrametric_strict (na : IsNonarchimedean f) variable [Semiring S] lemma Finset.Nonempty.map_sum_le_sup'_map - {α S : Type*} [Semiring S] [LinearOrder S] [AddCommMonoid α] (g : α → S) + {α S : Type*} [LinearOrder S] [AddCommMonoid α] (g : α → S) {ι : Type*} {s : Finset ι} (hs : s.Nonempty) (f : ι → α) (na : ∀ a b, g (a + b) ≤ max (g a) (g b)) : g (∑ i ∈ s, f i) ≤ s.sup' hs fun x ↦ g (f x) := by @@ -226,6 +239,30 @@ lemma gaussNorm_le_mul (vMulEq : ∀ a b, v (a * b) = v a * v b) rw [antidiagonal_dominant v f g i₀ j₀ vna vMulEq vNeg hdom'] _ ≤ gaussNorm v c (f * g) := le_gaussNorm v c (f * g) hbfg (i₀ + j₀) +lemma gaussNorm_mul_eq_mul (f g : MvPowerSeries σ R) (hf : HasGaussNorm v c f) + (hg : HasGaussNorm v c g) (hfg : HasGaussNorm v c (f * g)) + (vNonneg : ∀ a, v a ≥ 0) (vZero : v 0 = 0) (vNA : IsNonarchimedean v) + (vMulEq : ∀ (a b : R), v (a * b) = v a * v b) (vNeg : ∀ (a : R), v (-a) = v a) + (h_eq_zero : ∀ (x : R), v x = 0 → x = 0) (hc : ∀ (i : σ), 0 < c i) + (hdom : ∃ i j, AchievesGaussNorm v c f i ∧ AchievesGaussNorm v c g j ∧ + ∀ p ∈ Finset.antidiagonal (i + j), p ≠ (i, j) → v (coeff p.1 f * coeff p.2 g) < + v (coeff i f) * v (coeff j g)) : + gaussNorm v c (f * g) = gaussNorm v c f * gaussNorm v c g := by + by_cases hf' : f = 0 + · simp [hf', gaussNorm_zero v c vZero] + by_cases hg' : g = 0 + · simp [hg', gaussNorm_zero v c vZero] + have hf1 : gaussNorm v c f ≠ 0 := by + convert gaussNorm_eq_zero_iff v c f vZero vNonneg h_eq_zero hc hf + grind + have hg1 : gaussNorm v c g ≠ 0 := by + convert gaussNorm_eq_zero_iff v c g vZero vNonneg h_eq_zero hc hg + grind + apply ge_antisymm_iff.mpr + constructor + · exact gaussNorm_le_mul v c f g vMulEq vNA (by grind) hfg hdom + · exact gaussNorm_mul_le v c f g (StrongLT.le hc) vNonneg (by grind) vNA vZero hf hg + end absoluteValue end MvPowerSeries diff --git a/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean b/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean index b74e68083b7fd5..c47f513096cff1 100644 --- a/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean +++ b/Mathlib/RingTheory/MvPowerSeries/LinearTopology.lean @@ -61,13 +61,15 @@ noncomputable def basis (σ : Type*) (R : Type*) [Ring R] (Jd : TwoSidedIdeal R rw [coeff_mul] apply sum_mem rintro uv huv - exact TwoSidedIdeal.mul_mem_left _ _ _ (hg _ (le_trans (Finset.antidiagonal.snd_le huv) he))) + exact TwoSidedIdeal.mul_mem_left _ _ _ + (hg _ (le_trans (Finset.HasAntidiagonal.antidiagonal.snd_le huv) he))) (fun {f g} hf e he ↦ by classical rw [coeff_mul] apply sum_mem rintro uv huv - exact TwoSidedIdeal.mul_mem_right _ _ _ (hf _ (le_trans (Finset.antidiagonal.fst_le huv) he))) + exact TwoSidedIdeal.mul_mem_right _ _ _ + (hf _ (le_trans (Finset.HasAntidiagonal.antidiagonal.fst_le huv) he))) variable {σ : Type*} {R : Type*} [Ring R] diff --git a/Mathlib/RingTheory/MvPowerSeries/Restricted.lean b/Mathlib/RingTheory/MvPowerSeries/Restricted.lean new file mode 100644 index 00000000000000..72d90d9acdcb90 --- /dev/null +++ b/Mathlib/RingTheory/MvPowerSeries/Restricted.lean @@ -0,0 +1,112 @@ +/- +Copyright (c) 2025 William Coram. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: William Coram +-/ +module + +public import Mathlib.Algebra.Order.Antidiag.Tendsto +public import Mathlib.Algebra.Order.GroupWithZero.Finset +public import Mathlib.Analysis.Normed.Field.Basic +public import Mathlib.Analysis.Normed.Group.Ultra +public import Mathlib.RingTheory.MvPowerSeries.Basic + +/-! +# Multivariate restricted power series + +`IsRestricted` : We say a multivariate power series over a normed ring `R` is restricted for a +tuple `c` if `‖coeff t f‖ * ∏ i ∈ t.support, c i ^ t i → 0` under the cofinite filter. + +-/ + +@[expose] public section + +namespace MvPowerSeries + +open Filter +open scoped Topology Pointwise + +variable {R : Type*} [NormedRing R] {σ : Type*} + +/-- A multivariate powe0r series over a normed ring `R` is restricted for a + tuple `c` if `‖coeff t f‖ * ∏ i ∈ t.support, c i ^ t i → 0` under the cofinite filter. -/ +def IsRestricted (c : σ → ℝ) (f : MvPowerSeries σ R) := + Tendsto (fun (t : σ →₀ ℕ) ↦ ‖coeff t f‖ * t.prod (c · ^ ·)) cofinite (𝓝 0) + +@[simp] +lemma isRestricted_abs_iff (c : σ → ℝ) (f : MvPowerSeries σ R) : + IsRestricted |c| f ↔ IsRestricted c f := by + simp [IsRestricted, NormedAddGroup.tendsto_nhds_zero, Finsupp.prod] + +lemma isRestricted_zero (c : σ → ℝ) : IsRestricted c (0 : MvPowerSeries σ R) := by + simpa [IsRestricted] using tendsto_const_nhds + +lemma isRestricted_monomial (c : σ → ℝ) (n : σ →₀ ℕ) (a : R) : + IsRestricted c (monomial n a) := by + classical + refine tendsto_nhds_of_eventually_eq (Set.Subsingleton.finite ?_) + simp [Set.Subsingleton, coeff_monomial] + +lemma isRestricted_one (c : σ → ℝ) : IsRestricted c (1 : MvPowerSeries σ R) := + isRestricted_monomial c 0 1 + +lemma isRestricted_C (c : σ → ℝ) (a : R) : IsRestricted c (C a) := by + simpa [monomial_zero_eq_C_apply] using isRestricted_monomial c 0 a + +lemma isRestricted.add (c : σ → ℝ) {f g : MvPowerSeries σ R} (hf : IsRestricted c f) + (hg : IsRestricted c g) : IsRestricted c (f + g) := by + rw [← isRestricted_abs_iff, IsRestricted] at * + refine tendsto_const_nhds.squeeze (add_zero (0 : ℝ) ▸ hf.add hg) (fun n ↦ ?_) fun n ↦ ?_ + · dsimp [Finsupp.prod]; positivity -- TODO: add positivity extension for Finsupp.prod + rw [← add_mul] + exact mul_le_mul_of_nonneg_right (norm_add_le ..) (by dsimp [Finsupp.prod]; positivity) + +lemma isRestricted.neg (c : σ → ℝ) {f : MvPowerSeries σ R} (hf : IsRestricted c f) : + IsRestricted c (-f) := by + rw [← isRestricted_abs_iff, IsRestricted] at * + simpa [IsRestricted] using hf + +open IsUltrametricDist + +open Finset.HasAntidiagonal in +lemma tendsto_antidiagonal {M S : Type*} [AddMonoid M] [Finset.HasAntidiagonal M] [NormedRing S] + [IsUltrametricDist S] {C : M → ℝ} (hC : ∀ a b, C (a + b) = C a * C b) {f g : M → S} + (hf : Tendsto (fun i ↦ ‖f i‖ * C i) cofinite (𝓝 0)) + (hg : Tendsto (fun i ↦ ‖g i‖ * C i) cofinite (𝓝 0)) : + Tendsto (fun a ↦ ‖∑ p ∈ Finset.antidiagonal a, (f p.1 * g p.2)‖ * C a) cofinite (𝓝 0) := by + wlog hC' : 0 ≤ C generalizing C + · rw [tendsto_zero_iff_norm_tendsto_zero] + simpa using this (C := |C|) (by simp [hC]) (by simpa using hf.norm) + (by simpa using hg.norm) (fun _ => by simp) + refine .squeeze tendsto_const_nhds + (tendsto_sup'_antidiagonal_cofinite (tendsto_mul_cofinite_nhds_zero hf hg)) + (fun x ↦ mul_nonneg (by simp) (hC' x)) fun a ↦ ?_ + have : 0 ≤ C a := hC' a + grw [(nonempty_antidiagonal _).norm_sum_le_sup'_norm, Finset.sup'_mul₀ this] + refine Finset.sup'_mono_fun fun x hx ↦ ?_ + grw [mul_mul_mul_comm, ← hC, Finset.mem_antidiagonal.mp hx, ← norm_mul_le] + +lemma isRestricted.mul [IsUltrametricDist R] (c : σ → ℝ) {f g : MvPowerSeries σ R} + (hf : IsRestricted c f) (hg : IsRestricted c g) : IsRestricted c (f * g) := by + classical + rw [← isRestricted_abs_iff, IsRestricted] at * + exact tendsto_antidiagonal (by simp [Finsupp.prod_add_index', pow_add]) hf hg + +namespace IsRestricted + +/-- Restricted power series as an additive subgroup of `MvPowerSeries σ R`. -/ +protected def addSubgroup (c : σ → ℝ) : AddSubgroup (MvPowerSeries σ R) where + carrier := IsRestricted c + zero_mem' := isRestricted_zero c + add_mem' := isRestricted.add c + neg_mem' := isRestricted.neg c + +variable [IsUltrametricDist R] + +/-- Restricted power series as a subring of `MvPowerSeries σ R`. -/ +protected def subring (c : σ → ℝ) : Subring (MvPowerSeries σ R) where + __ := IsRestricted.addSubgroup c + one_mem' := isRestricted_one c + mul_mem' := isRestricted.mul c + +end MvPowerSeries.IsRestricted diff --git a/Mathlib/RingTheory/MvPowerSeries/Substitution.lean b/Mathlib/RingTheory/MvPowerSeries/Substitution.lean index 19bb2310ea5c87..4bf62eae37dcea 100644 --- a/Mathlib/RingTheory/MvPowerSeries/Substitution.lean +++ b/Mathlib/RingTheory/MvPowerSeries/Substitution.lean @@ -160,6 +160,12 @@ theorem hasSubst_of_constantCoeff_zero [Finite σ] lemma HasSubst.X_X {i j : σ} : HasSubst (S := R) ![X i, X j] := hasSubst_of_constantCoeff_zero (by simp) +lemma HasSubst.X_zero {i : σ} : HasSubst ![X i (R := R), 0] := + hasSubst_of_constantCoeff_zero (by simp) + +lemma HasSubst.zero_X {i : σ} : HasSubst ![0, X i (R := R)] := + hasSubst_of_constantCoeff_zero (by simp) + protected lemma HasSubst.pow {n : ℕ} (hn : n ≠ 0) {a : σ → MvPowerSeries τ S} (h : HasSubst a) : HasSubst (a ^ n) := hasSubstIdeal.pow_mem_of_mem h _ (by lia) @@ -351,6 +357,23 @@ theorem map_subst {a : σ → MvPowerSeries τ R} (ha : HasSubst a) {h : R →+* simp [smul_eq_mul, RingHom.toAddMonoidHom_eq_coe, AddMonoidHom.coe_coe, map_mul, ← coeff_map, Finsupp.prod] +lemma subst_zero_eq_C_constantCoeff {f : MvPowerSeries σ R} : + f.subst (0 : σ → MvPowerSeries τ S) = (C f.constantCoeff).map (algebraMap R S) := by + classical + ext n + rw [coeff_subst (by simp [hasSubst_def]), coeff_map, finsum_eq_single _ 0] + · by_cases hn : n = 0 + · simp [hn, Algebra.algebraMap_eq_smul_one] + simp [coeff_C_of_ne_zero hn, coeff_one, hn] + intro d hd + obtain ⟨i, hi⟩ : d.support.Nonempty := d.support_nonempty_iff.mpr hd + simp [Finsupp.prod, Finset.prod_eq_zero hi, coeff_zero, zero_pow <| d.mem_support_iff.mp hi] + +@[simp] +lemma subst_zero_of_constantCoeff_zero {f : MvPowerSeries σ R} (hf : f.constantCoeff = 0) : + f.subst (0 : σ → MvPowerSeries τ S) = 0 := by + simp [subst_zero_eq_C_constantCoeff, hf] + lemma HasSubst.cons_subst_zero_left {f : MvPowerSeries (Fin 2) R} (i j k : σ) (hF : constantCoeff f = 0) : HasSubst (![subst ![X i, X j] f, X k]) (S := R) := hasSubst_of_constantCoeff_zero fun s => by @@ -403,32 +426,30 @@ variable {υ : Type*} {T : Type*} [CommRing T] [Algebra R T] [Algebra S T] [IsScalarTower R S T] {b : τ → MvPowerSeries υ T} -theorem IsNilpotent_subst (ha : HasSubst a) - {f : MvPowerSeries σ R} (hf : IsNilpotent (constantCoeff f)) : - IsNilpotent (constantCoeff (substAlgHom ha f)) := by +lemma IsNilpotent_subst (ha : HasSubst a) + {f : MvPowerSeries σ R} (hf : IsNilpotent f.constantCoeff) : + IsNilpotent (constantCoeff (f.subst a)) := by classical - rw [coe_substAlgHom, constantCoeff_subst ha] - apply isNilpotent_finsum - intro d + rw [constantCoeff_subst ha] + refine isNilpotent_finsum fun d => ?_ by_cases hd : d = 0 · rw [← algebraMap_smul S, smul_eq_mul, mul_comm, ← smul_eq_mul, hd] apply IsNilpotent.smul simpa using IsNilpotent.map hf (algebraMap R S) - · apply IsNilpotent.smul - rw [← ne_eq, Finsupp.ne_iff] at hd - obtain ⟨t, hs⟩ := hd - rw [← Finsupp.prod_filter_mul_prod_filter_not (fun i ↦ i = t), map_mul, - mul_comm, ← smul_eq_mul] - apply IsNilpotent.smul - rw [Finsupp.prod_eq_single t] - · simpa using IsNilpotent.pow_of_pos (ha.const_coeff t) hs - · intro t' htt' ht' - simp [ht'] at htt' - · exact fun _ ↦ by rw [pow_zero] + obtain ⟨i, hi⟩ : d.support.Nonempty := d.support_nonempty_iff.mpr hd + rw [Finsupp.prod, map_prod, ← Finset.prod_erase_mul _ _ hi, ← algebraMap_smul S, + smul_eq_mul, ← mul_assoc, map_pow] + exact Commute.isNilpotent_mul_left (Commute.all _ _) + <| (IsNilpotent.pow_iff_pos (d.mem_support_iff.mp hi)).mpr (ha.const_coeff i) + +theorem IsNilpotent_substAlgHom (ha : HasSubst a) + {f : MvPowerSeries σ R} (hf : IsNilpotent (constantCoeff f)) : + IsNilpotent (constantCoeff (substAlgHom ha f)) := by + simpa using IsNilpotent_subst ha hf theorem HasSubst.comp (ha : HasSubst a) (hb : HasSubst b) : HasSubst (fun s ↦ substAlgHom hb (a s)) where - const_coeff s := IsNilpotent_subst hb (ha.const_coeff s) + const_coeff s := IsNilpotent_substAlgHom hb (ha.const_coeff s) coeff_zero := by letI : UniformSpace S := ⊥ letI : UniformSpace T := ⊥ diff --git a/Mathlib/RingTheory/MvPowerSeries/Trunc.lean b/Mathlib/RingTheory/MvPowerSeries/Trunc.lean index 2816b558c8d744..0f01f106605e17 100644 --- a/Mathlib/RingTheory/MvPowerSeries/Trunc.lean +++ b/Mathlib/RingTheory/MvPowerSeries/Trunc.lean @@ -126,16 +126,24 @@ theorem truncFinset_map [CommSemiring S] (f : R →+* S) (p : MvPowerSeries σ R ext x by_cases x ∈ s <;> grind [coeff_map, MvPolynomial.coeff_map] -theorem coeff_truncFinset_mul_truncFinset_eq_coeff_mul (hs : IsLowerSet (s : Set (σ →₀ ℕ))) - {x : σ →₀ ℕ} (f g : MvPowerSeries σ R) (hx : x ∈ s) : - (truncFinset R s f * truncFinset R s g).coeff x = coeff x (f * g) := by +/-- A coefficient of a product of finset-truncated power series equals the coefficient of the +untruncated product, with the two truncation finsets `s` and `t` allowed to differ. -/ +theorem coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ {t : Finset (σ →₀ ℕ)} + (hs : IsLowerSet (s : Set (σ →₀ ℕ))) (ht : IsLowerSet (t : Set (σ →₀ ℕ))) + {x : σ →₀ ℕ} (f g : MvPowerSeries σ R) (hxs : x ∈ s) (hxt : x ∈ t) : + (truncFinset R s f * truncFinset R t g).coeff x = coeff x (f * g) := by classical simp only [MvPowerSeries.coeff_mul, MvPolynomial.coeff_mul] apply sum_congr rfl rintro ⟨i, j⟩ hij simp only [mem_antidiagonal] at hij - rw [coeff_truncFinset_of_mem _ (hs (show i ≤ x by simp [← hij]) hx), - coeff_truncFinset_of_mem _ (hs (show j ≤ x by simp [← hij]) hx)] + rw [coeff_truncFinset_of_mem _ (hs (show i ≤ x by simp [← hij]) hxs), + coeff_truncFinset_of_mem _ (ht (show j ≤ x by simp [← hij]) hxt)] + +theorem coeff_truncFinset_mul_truncFinset_eq_coeff_mul (hs : IsLowerSet (s : Set (σ →₀ ℕ))) + {x : σ →₀ ℕ} (f g : MvPowerSeries σ R) (hx : x ∈ s) : + (truncFinset R s f * truncFinset R s g).coeff x = coeff x (f * g) := + coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ hs hs f g hx hx theorem truncFinset_truncFinset_pow (hs : IsLowerSet (s : Set (σ →₀ ℕ))) {k : ℕ} (hk : 1 ≤ k) (p : MvPowerSeries σ R) : truncFinset R s ((truncFinset R s p) ^ k) = @@ -200,6 +208,21 @@ theorem trunc_C_mul (n : σ →₀ ℕ) (a : R) (p : MvPowerSeries σ R) : theorem trunc_map [CommSemiring S] (n : σ →₀ ℕ) (f : R →+* S) (p : MvPowerSeries σ R) : trunc S n (map f p) = MvPolynomial.map f (trunc R n p) := truncFinset_map f p +/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated +product, with the two truncation levels `n₁` and `n₂` allowed to differ. -/ +theorem coeff_trunc_mul_trunc_eq_coeff_mul₂ (n₁ n₂ : σ →₀ ℕ) + (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h₁ : m < n₁) (h₂ : m < n₂) : + (trunc R n₁ f * trunc R n₂ g).coeff m = coeff m (f * g) := + coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ (by grind [IsLowerSet]) (by grind [IsLowerSet]) + f g (by simpa) (by simpa) + +/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated +product. Both factors are truncated at the same level `n`. -/ +theorem coeff_trunc_mul_trunc_eq_coeff_mul (n : σ →₀ ℕ) + (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h : m < n) : + (trunc R n f * trunc R n g).coeff m = coeff m (f * g) := + coeff_trunc_mul_trunc_eq_coeff_mul₂ n n f g h h + end TruncLT section TruncLE @@ -232,11 +255,20 @@ theorem trunc'_one (n : σ →₀ ℕ) : trunc' R n 1 = 1 := truncFinset_one (by theorem trunc'_C (n : σ →₀ ℕ) (a : R) : trunc' R n (C a) = MvPolynomial.C a := truncFinset_C (by simp) a -/-- Coefficients of the truncation of a product of two multivariate power series -/ +/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated +product, with the two truncation levels `n₁` and `n₂` allowed to differ. -/ +theorem coeff_trunc'_mul_trunc'_eq_coeff_mul₂ (n₁ n₂ : σ →₀ ℕ) + (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h₁ : m ≤ n₁) (h₂ : m ≤ n₂) : + (trunc' R n₁ f * trunc' R n₂ g).coeff m = coeff m (f * g) := + coeff_truncFinset_mul_truncFinset_eq_coeff_mul₂ (by grind [IsLowerSet]) (by grind [IsLowerSet]) + f g (by simpa) (by simpa) + +/-- A coefficient of a product of truncated power series equals the coefficient of the untruncated +product. Both factors are truncated at the same level `n`. -/ theorem coeff_trunc'_mul_trunc'_eq_coeff_mul (n : σ →₀ ℕ) (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} (h : m ≤ n) : (trunc' R n f * trunc' R n g).coeff m = coeff m (f * g) := - coeff_truncFinset_mul_truncFinset_eq_coeff_mul (by intro; grind) f g (by simpa) + coeff_trunc'_mul_trunc'_eq_coeff_mul₂ n n f g h h @[deprecated coeff_trunc'_mul_trunc'_eq_coeff_mul (since := "2026-02-20")] theorem coeff_mul_eq_coeff_trunc'_mul_trunc' (n : σ →₀ ℕ) (f g : MvPowerSeries σ R) {m : σ →₀ ℕ} diff --git a/Mathlib/RingTheory/NoetherNormalization.lean b/Mathlib/RingTheory/NoetherNormalization.lean index 2e99d7ec0ca2a8..c30969e30f41e9 100644 --- a/Mathlib/RingTheory/NoetherNormalization.lean +++ b/Mathlib/RingTheory/NoetherNormalization.lean @@ -10,6 +10,7 @@ public import Mathlib.Data.List.Indexes public import Mathlib.RingTheory.IntegralClosure.IsIntegralClosure.Basic /-! # Noether normalization lemma + This file contains a proof by Nagata of the Noether normalization lemma. ## Main Results @@ -278,7 +279,7 @@ theorem exists_integral_inj_algHom_of_fg : ∃ s, ∃ g : (MvPolynomial (Fin s) set ϕ := quotientKerAlgEquivOfSurjective fsurj obtain ⟨s, _, g, injg, intg⟩ := exists_integral_inj_algHom_of_quotient (ker f) (ker_ne_top _) use s, ϕ.toAlgHom.comp g - simp only [AlgHom.coe_comp, AlgEquiv.coe_algHom, EmbeddingLike.comp_injective, + simp only [AlgHom.coe_comp, AlgEquiv.coe_toAlgHom, EmbeddingLike.comp_injective, AlgHom.toRingHom_eq_coe] exact ⟨injg, intg.trans _ _ (isIntegral_of_surjective _ ϕ.surjective)⟩ diff --git a/Mathlib/RingTheory/Noetherian/Basic.lean b/Mathlib/RingTheory/Noetherian/Basic.lean index 8e31fb6142f586..e2457ce2c3363d 100644 --- a/Mathlib/RingTheory/Noetherian/Basic.lean +++ b/Mathlib/RingTheory/Noetherian/Basic.lean @@ -382,6 +382,12 @@ lemma FG.of_le [IsNoetherianRing R] {S T : Submodule R M} (hT : T.FG) (hST : S rw [← Module.Finite.iff_fg] at hT exact FG.of_le_of_isNoetherian hST +/-- If `S` is disjoint from `T` and `M ⧸ T` is a noetherian module, then `S` is FG. +See also `Submodule.CoFG.fg_of_disjoint`. -/ +theorem FG.of_disjoint_of_isNoetherian_quotient {S T : Submodule R M} [IsNoetherian R (M ⧸ T)] + (hST : Disjoint S T) : S.FG := + Module.Finite.iff_fg.mp <| .of_injective (T.mkQ.domRestrict S) (by simp [hST]) + end Submodule universe w v u diff --git a/Mathlib/RingTheory/Nullstellensatz.lean b/Mathlib/RingTheory/Nullstellensatz.lean index 104ec198650cdd..34fd73c572fca6 100644 --- a/Mathlib/RingTheory/Nullstellensatz.lean +++ b/Mathlib/RingTheory/Nullstellensatz.lean @@ -11,6 +11,7 @@ public import Mathlib.RingTheory.Spectrum.Prime.Basic /-! # Nullstellensatz + This file establishes a version of Hilbert's classical Nullstellensatz for `MvPolynomial`s. The main statement of the theorem is `MvPolynomial.vanishingIdeal_zeroLocus_eq_radical`. diff --git a/Mathlib/RingTheory/OrderOfVanishing/Noetherian.lean b/Mathlib/RingTheory/OrderOfVanishing/Noetherian.lean index f168d029b3da06..29fed9bd750345 100644 --- a/Mathlib/RingTheory/OrderOfVanishing/Noetherian.lean +++ b/Mathlib/RingTheory/OrderOfVanishing/Noetherian.lean @@ -18,7 +18,7 @@ public import Mathlib.RingTheory.Valuation.Discrete.IsDiscreteValuationRing # Order of vanishing in Noetherian rings. In this file we define various properties of the order of vanishing in Noetherian rings, including - some API for computing the order of vanishing in discrete valuation rings. +some API for computing the order of vanishing in discrete valuation rings. -/ @[expose] public section diff --git a/Mathlib/RingTheory/Perfectoid/FontaineTheta.lean b/Mathlib/RingTheory/Perfectoid/FontaineTheta.lean index 03f25093599e07..98f4f4b1358bf5 100644 --- a/Mathlib/RingTheory/Perfectoid/FontaineTheta.lean +++ b/Mathlib/RingTheory/Perfectoid/FontaineTheta.lean @@ -12,6 +12,7 @@ public import Mathlib.RingTheory.WittVector.TeichmullerSeries /-! # Fontaine's θ map + In this file, we define Fontaine's `θ` map, which is a ring homomorphism from the Witt vector `𝕎 R♭` of the tilt of a perfectoid ring `R` to `R` itself. Our definition of `θ` does not require that `R` is perfectoid in the first place. diff --git a/Mathlib/RingTheory/Polynomial/Basic.lean b/Mathlib/RingTheory/Polynomial/Basic.lean index c186c48bb63ee7..96e51ee43704b3 100644 --- a/Mathlib/RingTheory/Polynomial/Basic.lean +++ b/Mathlib/RingTheory/Polynomial/Basic.lean @@ -763,17 +763,8 @@ theorem prime_rename_iff (s : Set σ) {p : MvPolynomial s R} : (renameEquiv R <| (Equiv.sumComm (↥sᶜ) s).trans <| Equiv.Set.sumCompl s) have : (rename (↑)).toRingHom = eqv.toAlgHom.toRingHom.comp C := by apply ringHom_ext - · intro - simp only [eqv, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, rename_C, - AlgEquiv.toAlgHom_toRingHom, RingHom.coe_comp, AlgEquiv.coe_trans, - Function.comp_apply, MvPolynomial.sumAlgEquiv_symm_apply, iterToSum_C_C, - renameEquiv_apply, Equiv.coe_trans, Equiv.sumComm_apply] - · intro - simp only [eqv, AlgHom.toRingHom_eq_coe, RingHom.coe_coe, rename_X, - AlgEquiv.toAlgHom_toRingHom, RingHom.coe_comp, AlgEquiv.coe_trans, - Function.comp_apply, MvPolynomial.sumAlgEquiv_symm_apply, iterToSum_C_X, - renameEquiv_apply, Equiv.coe_trans, Equiv.sumComm_apply, Sum.swap_inr, - Equiv.Set.sumCompl_apply_inl] + · simp [eqv] + · simp [eqv] apply_fun (· p) at this simp only [AlgHom.toRingHom_eq_coe, RingHom.coe_coe, AlgEquiv.toAlgHom_toRingHom, RingHom.coe_comp, Function.comp_apply] at this @@ -935,7 +926,7 @@ lemma aeval_natDegree_le {R : Type*} [CommSemiring R] {m n : ℕ} apply (Polynomial.natDegree_sum_le _ _).trans apply Finset.sup_le intro d hd - simp_rw [Function.comp_apply, ← C_eq_algebraMap] + simp_rw [Function.comp_apply, ← Polynomial.C_eq_algebraMap] apply (Polynomial.natDegree_C_mul_le _ _).trans apply (Polynomial.natDegree_prod_le _ _).trans have : ∑ i ∈ d.support, (d i) * n ≤ m * n := by diff --git a/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean b/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean index aa330d8e13e4ea..05e37ed8dd0363 100644 --- a/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean +++ b/Mathlib/RingTheory/Polynomial/Cyclotomic/Basic.lean @@ -656,7 +656,7 @@ theorem separable_cyclotomic (n : ℕ) (K : Type*) [Field K] [NeZero (n : K)] : theorem squarefree_cyclotomic (n : ℕ) (K : Type*) [Field K] [NeZero (n : K)] : Squarefree (cyclotomic n K) := - (separable_cyclotomic n K).squarefree + (separable_cyclotomic n K).squarefree end miscellaneous diff --git a/Mathlib/RingTheory/Polynomial/Cyclotomic/Eval.lean b/Mathlib/RingTheory/Polynomial/Cyclotomic/Eval.lean index 6214c9b651a270..9e7bea20b4441b 100644 --- a/Mathlib/RingTheory/Polynomial/Cyclotomic/Eval.lean +++ b/Mathlib/RingTheory/Polynomial/Cyclotomic/Eval.lean @@ -13,6 +13,7 @@ public import Mathlib.Analysis.Complex.Arg /-! # Evaluating cyclotomic polynomials + This file states some results about evaluating cyclotomic polynomials in various different ways. ## Main definitions diff --git a/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean b/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean index cb48fd6da6f6c2..2d1410358a3ebb 100644 --- a/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean +++ b/Mathlib/RingTheory/Polynomial/Cyclotomic/Factorization.lean @@ -64,9 +64,9 @@ private theorem natDegree_of_dvd_cyclotomic_of_irreducible_of_monic (hP : P ∣ ⟨n, pos_of_ne_zero (fun h0 ↦ by simp [h0, hp.out.ne_one] at hn), hζ.pow_eq_one⟩ refine dvd_antisymm - (orderOf_dvd_iff_pow_eq_one.mpr <| AlgEquiv.coe_algHom_injective <| pB.algHom_ext ?_) + (orderOf_dvd_iff_pow_eq_one.mpr <| AlgEquiv.coe_toAlgHom_injective <| pB.algHom_ext ?_) (orderOf_dvd_iff_pow_eq_one.mpr <| Units.ext ?_) - · simp only [AlgEquiv.coe_algHom, AlgEquiv.coe_pow, AlgEquiv.one_apply, + · simp only [AlgEquiv.coe_toAlgHom, AlgEquiv.coe_pow, AlgEquiv.one_apply, coe_frobeniusAlgEquivOfAlgebraic, pow_iterate, hK] nth_rewrite 2 [← pow_one pB.gen] rw [powerBasis_gen hPirr.ne_zero, hζ'.pow_eq_pow_iff_modEq, ← hζ.eq_orderOf, diff --git a/Mathlib/RingTheory/Polynomial/Eisenstein/Basic.lean b/Mathlib/RingTheory/Polynomial/Eisenstein/Basic.lean index ae2a68d66e31c3..6245e18877a991 100644 --- a/Mathlib/RingTheory/Polynomial/Eisenstein/Basic.lean +++ b/Mathlib/RingTheory/Polynomial/Eisenstein/Basic.lean @@ -11,6 +11,7 @@ public import Mathlib.RingTheory.Polynomial.ScaleRoots /-! # Eisenstein polynomials + Given an ideal `𝓟` of a commutative semiring `R`, we say that a polynomial `f : R[X]` is *Eisenstein at `𝓟`* if `f.leadingCoeff ∉ 𝓟`, `∀ n, n < f.natDegree → f.coeff n ∈ 𝓟` and `f.coeff 0 ∉ 𝓟 ^ 2`. In this file we gather miscellaneous results about Eisenstein polynomials. diff --git a/Mathlib/RingTheory/Polynomial/Eisenstein/IsIntegral.lean b/Mathlib/RingTheory/Polynomial/Eisenstein/IsIntegral.lean index 68d0a280a013c0..c8a0f9e6b53d1f 100644 --- a/Mathlib/RingTheory/Polynomial/Eisenstein/IsIntegral.lean +++ b/Mathlib/RingTheory/Polynomial/Eisenstein/IsIntegral.lean @@ -12,6 +12,7 @@ public import Mathlib.RingTheory.Polynomial.Cyclotomic.Expand /-! # Eisenstein polynomials + In this file we gather more miscellaneous results about Eisenstein polynomials ## Main results diff --git a/Mathlib/RingTheory/Polynomial/GaussNorm.lean b/Mathlib/RingTheory/Polynomial/GaussNorm.lean index 04250ee856e304..4b6d9d9f6afa1a 100644 --- a/Mathlib/RingTheory/Polynomial/GaussNorm.lean +++ b/Mathlib/RingTheory/Polynomial/GaussNorm.lean @@ -10,6 +10,7 @@ public import Mathlib.RingTheory.PowerSeries.GaussNorm /-! # Gauss norm for polynomials + This file defines the Gauss norm for polynomials. Given a polynomial `p` in `R[X]`, a function `v : R → ℝ` and a real number `c`, the Gauss norm is defined as the supremum of the set of all values of `v (p.coeff i) * c ^ i` for all `i` in the support of `p`. diff --git a/Mathlib/RingTheory/PolynomialLaw/Basic.lean b/Mathlib/RingTheory/PolynomialLaw/Basic.lean index 586d31ab7c3f83..1a1843e362c313 100644 --- a/Mathlib/RingTheory/PolynomialLaw/Basic.lean +++ b/Mathlib/RingTheory/PolynomialLaw/Basic.lean @@ -165,8 +165,10 @@ instance : AddCommMonoid (M →ₚₗ[R] N) where zero_add f := by ext; simp only [add_def, zero_add, zero_def] add_zero f := by ext; simp only [add_def, add_zero, zero_def] nsmul n f := (n : R) • f - nsmul_zero f := by simp only [Nat.cast_zero, zero_smul f] - nsmul_succ n f := by simp only [Nat.cast_add, Nat.cast_one, add_smul, one_smul] + nsmul_zero f := by simp_rw [HSMul.hSMul, SMul.smul]; simp only [Nat.cast_zero, zero_smul f] + nsmul_succ n f := by + simp_rw [HSMul.hSMul, SMul.smul] + simp only [Nat.cast_add, Nat.cast_one, add_smul, one_smul] add_comm f g := by ext; simp only [add_def, add_comm] instance : Module R (M →ₚₗ[R] N) where @@ -195,15 +197,17 @@ theorem neg_def (S : Type u) [CommSemiring S] [Algebra R S] : instance : AddCommGroup (M →ₚₗ[R] N) where zsmul n f := (n : R) • f - zsmul_zero' f := by simp only [Int.cast_zero, zero_smul] - zsmul_succ' n f := by simp only [Nat.cast_succ, Int.cast_add, Int.cast_natCast, - Int.cast_one, add_smul, _root_.one_smul] + zsmul_zero' f := by simp_rw [HSMul.hSMul, SMul.smul]; simp only [Int.cast_zero, zero_smul] + zsmul_succ' n f := by + simp_rw [HSMul.hSMul, SMul.smul] + simp only [Nat.cast_succ, Int.cast_add, Int.cast_natCast, Int.cast_one, add_smul, one_smul] zsmul_neg' n f := by + simp_rw [HSMul.hSMul, SMul.smul] ext S _ _ m rw [neg_def] - simp only [Int.cast_negSucc, Nat.cast_add, Nat.cast_one, neg_add_rev, _root_.add_smul, + simp only [Int.cast_negSucc, Nat.cast_add, Nat.cast_one, neg_add_rev, add_smul, add_def_apply, smul_def_apply, Nat.succ_eq_add_one, Int.cast_add, Int.cast_natCast, - Int.cast_one, _root_.one_smul, add_def, smul_def, Pi.smul_apply, Pi.add_apply, smul_add, + Int.cast_one, one_smul, add_def, smul_def, Pi.smul_apply, Pi.add_apply, smul_add, smul_smul, neg_mul, one_mul] rw [add_comm] neg_add_cancel f := by diff --git a/Mathlib/RingTheory/PowerBasis.lean b/Mathlib/RingTheory/PowerBasis.lean index d2d640b5e11137..4b16f4168d621b 100644 --- a/Mathlib/RingTheory/PowerBasis.lean +++ b/Mathlib/RingTheory/PowerBasis.lean @@ -115,7 +115,7 @@ theorem mem_span_pow {x y : S} {d : ℕ} (hd : d ≠ 0) : · rintro ⟨f, h, hy⟩ refine ⟨f, ?_, hy⟩ by_cases hf : f = 0 - · simp only [hf, natDegree_zero, degree_zero] at h ⊢ + · simp only [hf, natDegree_zero, Polynomial.degree_zero] at h ⊢ first | exact lt_of_le_of_ne (Nat.zero_le d) hd.symm | exact WithBot.bot_lt_coe d simpa [degree_eq_natDegree hf] using h diff --git a/Mathlib/RingTheory/PowerSeries/Basic.lean b/Mathlib/RingTheory/PowerSeries/Basic.lean index b9c69c2a9e360c..6363eb10d7665b 100644 --- a/Mathlib/RingTheory/PowerSeries/Basic.lean +++ b/Mathlib/RingTheory/PowerSeries/Basic.lean @@ -88,6 +88,10 @@ theorem coeff_def {s : Unit →₀ ℕ} {n : ℕ} (h : s () = n) : coeff (R := R) n = MvPowerSeries.coeff s := by rw [coeff, ← h, ← Finsupp.unique_single s] +@[simp] +lemma coeff_coeToMvPowerSeries {f : R⟦X⟧} (n : ℕ) : + MvPowerSeries.coeff (Finsupp.single () n) f = f.coeff n := rfl + /-- Two formal power series are equal if all their coefficients are equal. -/ @[ext] theorem ext {φ ψ : R⟦X⟧} (h : ∀ n, coeff n φ = coeff n ψ) : φ = ψ := @@ -228,12 +232,12 @@ theorem X_ne_zero [Nontrivial R] : (X : R⟦X⟧) ≠ 0 := fun H => by theorem X_pow_eq (n : ℕ) : (X : R⟦X⟧) ^ n = monomial n 1 := MvPowerSeries.X_pow_eq _ n +@[simp, grind =] theorem coeff_X_pow (m n : ℕ) : coeff m ((X : R⟦X⟧) ^ n) = if m = n then 1 else 0 := by rw [X_pow_eq, coeff_monomial] -@[simp] theorem coeff_X_pow_self (n : ℕ) : coeff n ((X : R⟦X⟧) ^ n) = 1 := by - rw [coeff_X_pow, if_pos rfl] + simp @[simp] theorem coeff_one (n : ℕ) : coeff n (1 : R⟦X⟧) = if n = 0 then 1 else 0 := diff --git a/Mathlib/RingTheory/PowerSeries/Binomial.lean b/Mathlib/RingTheory/PowerSeries/Binomial.lean index ef0f975ab87e94..d6ed9c2db77f59 100644 --- a/Mathlib/RingTheory/PowerSeries/Binomial.lean +++ b/Mathlib/RingTheory/PowerSeries/Binomial.lean @@ -11,6 +11,7 @@ public import Mathlib.Tactic.SuppressCompilation /-! # Binomial Power Series + We introduce formal power series of the form `(1 + X) ^ r`, where `r` is an element of a commutative binomial ring `R`. diff --git a/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean b/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean index aa70f8948a49ca..cff3f91d0f39c4 100644 --- a/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean +++ b/Mathlib/RingTheory/PowerSeries/CoeffMulMem.lean @@ -45,8 +45,8 @@ theorem coeff_mul_mem_ideal_mul_ideal_of_coeff_mem_ideal (hf : ∀ i ≤ n, coef (hg : ∀ i ≤ n, coeff i g ∈ J) : ∀ i ≤ n, coeff i (f * g) ∈ I * J := fun i hi ↦ by rw [coeff_mul] exact Ideal.sum_mem _ fun p hp ↦ Ideal.mul_mem_mul - (hf _ ((Finset.antidiagonal.fst_le hp).trans hi)) - (hg _ ((Finset.antidiagonal.snd_le hp).trans hi)) + (hf _ ((Finset.HasAntidiagonal.antidiagonal.fst_le hp).trans hi)) + (hg _ ((Finset.HasAntidiagonal.antidiagonal.snd_le hp).trans hi)) theorem coeff_mul_mem_ideal_mul_ideal_of_coeff_mem_ideal' (hf : ∀ i, coeff i f ∈ I) (hg : ∀ i, coeff i g ∈ J) : ∀ i, coeff i (f * g) ∈ I * J := diff --git a/Mathlib/RingTheory/PowerSeries/Evaluation.lean b/Mathlib/RingTheory/PowerSeries/Evaluation.lean index c09f9380cc32d7..8e4a47c6184849 100644 --- a/Mathlib/RingTheory/PowerSeries/Evaluation.lean +++ b/Mathlib/RingTheory/PowerSeries/Evaluation.lean @@ -179,7 +179,7 @@ theorem hasSum_eval₂ (hφ : Continuous φ) (ha : HasEval a) (f : PowerSeries R have := MvPowerSeries.hasSum_eval₂ hφ (hasEval ha) f simp only [PowerSeries.eval₂] rw [← (Finsupp.single_injective ()).hasSum_iff] at this - · convert! this; simp; congr + · convert this; simp · intro d hd exact False.elim (hd ⟨d (), by ext; simp⟩) diff --git a/Mathlib/RingTheory/PowerSeries/GaussNorm.lean b/Mathlib/RingTheory/PowerSeries/GaussNorm.lean index b62a12ac78d3e8..7c2192ae7391c3 100644 --- a/Mathlib/RingTheory/PowerSeries/GaussNorm.lean +++ b/Mathlib/RingTheory/PowerSeries/GaussNorm.lean @@ -11,6 +11,7 @@ public import Mathlib.RingTheory.MvPowerSeries.GaussNorm /-! # Gauss norm for power series + This file defines the Gauss norm for power series using the gaussNorm for multivariate power series. Given a power series `f` in `R⟦X⟧`, a function `v : R → ℝ` and a real number `c`, the Gauss norm is defined as the supremum of the set of all values of `v (f.coeff i) * c ^ i` for all `i : ℕ`. diff --git a/Mathlib/RingTheory/PowerSeries/Substitution.lean b/Mathlib/RingTheory/PowerSeries/Substitution.lean index 1ae3a665099d6d..594ec0f9357e8b 100644 --- a/Mathlib/RingTheory/PowerSeries/Substitution.lean +++ b/Mathlib/RingTheory/PowerSeries/Substitution.lean @@ -196,6 +196,15 @@ theorem subst_sub (ha : HasSubst a) (f g : PowerSeries R) : subst a (f - g) = subst a f - subst a g := by rw [← coe_substAlgHom ha, map_sub] +lemma subst_zero_eq_C_constantCoeff {f : PowerSeries R} : + f.subst 0 = (MvPowerSeries.C f.constantCoeff (σ := τ)).map (algebraMap R S) := + MvPowerSeries.subst_zero_eq_C_constantCoeff + +@[simp] +theorem subst_zero_of_constantCoeff_zero {f : PowerSeries R} (hf : f.constantCoeff = 0) : + subst (0 : MvPowerSeries τ S) f = 0 := + MvPowerSeries.subst_zero_of_constantCoeff_zero hf + theorem subst_pow (ha : HasSubst a) (f : PowerSeries R) (n : ℕ) : subst a (f ^ n) = (subst a f) ^ n := by rw [← coe_substAlgHom ha, map_pow] @@ -362,7 +371,7 @@ end theorem HasSubst.comp {a : PowerSeries S} (ha : HasSubst a) {b : MvPowerSeries υ T} (hb : HasSubst b) : HasSubst (substAlgHom hb a) := - MvPowerSeries.IsNilpotent_subst hb.const ha + MvPowerSeries.IsNilpotent_substAlgHom hb.const ha variable {a : PowerSeries S} {b : MvPowerSeries υ T} {a' : MvPowerSeries τ S} {b' : τ → MvPowerSeries υ T} [IsScalarTower R S T] diff --git a/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean b/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean index a0dc12fa468ee4..ac4b409fb6d2a3 100644 --- a/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean +++ b/Mathlib/RingTheory/PowerSeries/WeierstrassPreparation.lean @@ -746,9 +746,9 @@ theorem IsWeierstrassDivision.isUnit_of_map_ne_zero · rw [coeff_of_lt_order p.1 ?_] · rw [zero_mul] · rw [← ENat.lt_lift_iff (h := order_finite_iff_ne_zero.2 hg), ENat.lift_eq_toNat_of_lt_top] - refine (Finset.antidiagonal.fst_le hp).lt_of_ne ?_ + refine (Finset.HasAntidiagonal.antidiagonal.fst_le hp).lt_of_ne ?_ contrapose hnotMem - rwa [Finset.mem_singleton, Finset.antidiagonal_congr hp (by simp)] + rwa [Finset.mem_singleton, Finset.HasAntidiagonal.antidiagonal_congr hp (by simp)] theorem IsWeierstrassDivision.isWeierstrassFactorization {g q : A⟦X⟧} {r : A[X]} (hg : g.map (IsLocalRing.residue A) ≠ 0) diff --git a/Mathlib/RingTheory/Prime.lean b/Mathlib/RingTheory/Prime.lean index d738fa082bb8bd..44dbde483bb308 100644 --- a/Mathlib/RingTheory/Prime.lean +++ b/Mathlib/RingTheory/Prime.lean @@ -13,6 +13,7 @@ public import Mathlib.Algebra.BigOperators.Group.Finset.Basic /-! # Prime elements in rings + This file contains lemmas about prime elements of commutative rings. -/ diff --git a/Mathlib/RingTheory/QuasiFinite/Basic.lean b/Mathlib/RingTheory/QuasiFinite/Basic.lean index cff1eb8679363a..08f45dedcf66ea 100644 --- a/Mathlib/RingTheory/QuasiFinite/Basic.lean +++ b/Mathlib/RingTheory/QuasiFinite/Basic.lean @@ -412,16 +412,15 @@ omit [Algebra S T] in lemma QuasiFiniteAt.eq_of_le_of_under_eq {P Q : Ideal S} [P.IsPrime] [Q.IsPrime] (h₁ : P ≤ Q) (h₂ : P.under R = Q.under R) [QuasiFiniteAt R Q] : P = Q := by - have : Disjoint (Q.primeCompl : Set S) P := by simpa [Set.disjoint_iff, Set.ext_iff, not_imp_comm] - have inst := IsLocalization.isPrime_of_isPrime_disjoint _ (Localization.AtPrime Q) P ‹_› this + have := Q.isPrime_map_of_isLocalizationAtPrime h₁ (S := Localization.AtPrime Q) have H := QuasiFinite.eq_of_le_of_under_eq (R := R) (Ideal.map (algebraMap S (Localization.AtPrime Q)) P) _ (IsLocalRing.le_maximalIdeal_of_isPrime _) (by convert! h₂ <;> rw [← Ideal.under_under (B := S)] - · rw [IsLocalization.under_map_of_isPrime_disjoint Q.primeCompl _ ‹P.IsPrime› this] + · rw [Q.under_map_of_isLocalizationAtPrime h₁] · rw [Localization.AtPrime.under_maximalIdeal]) rw [← Localization.AtPrime.under_maximalIdeal (I := Q), ← H, - IsLocalization.under_map_of_isPrime_disjoint Q.primeCompl _ ‹P.IsPrime› this] + Q.under_map_of_isLocalizationAtPrime h₁] instance (p : Ideal R) [p.IsPrime] (P : Ideal S) [P.IsPrime] [P.LiesOver p] [QuasiFiniteAt R P] [Algebra (Localization.AtPrime p) (Localization.AtPrime P)] diff --git a/Mathlib/RingTheory/Radical/Basic.lean b/Mathlib/RingTheory/Radical/Basic.lean index 66ee3710ca9be3..2b536c44fb969e 100644 --- a/Mathlib/RingTheory/Radical/Basic.lean +++ b/Mathlib/RingTheory/Radical/Basic.lean @@ -56,6 +56,12 @@ open scoped Classical in def primeFactors (a : M) : Finset M := (normalizedFactors a).toFinset +@[simp] +theorem toFinset_normalizedFactors [DecidableEq M] : + (normalizedFactors a).toFinset = primeFactors a := by + unfold primeFactors + convert rfl + lemma mem_primeFactors : a ∈ primeFactors b ↔ a ∈ normalizedFactors b := by simp only [primeFactors, Multiset.mem_toFinset] @@ -296,6 +302,22 @@ theorem radical_dvd_iff_primeFactors_subset (hb : b ≠ 0) : rw [← dvd_radical_iff isRadical_radical hb, radical_dvd_radical_iff_primeFactors_subset_primeFactors] +theorem exists_dvd_pow_iff_radical_dvd (ha : a ≠ 0) : (∃ n, a ∣ b ^ n) ↔ radical a ∣ b := by + rcases eq_or_ne b 0 with (rfl | hb) + · exact ⟨by simp, fun _ ↦ ⟨1, by simp⟩⟩ + refine ⟨fun ⟨n, hdvd⟩ ↦ ?_, fun h ↦ ⟨normalizedFactors a |>.card, ?_⟩⟩ + · rcases eq_or_ne n 0 with (rfl | hn) + · simp [radical_of_isUnit <| isUnit_of_dvd_one <| pow_zero b ▸ hdvd] + grw [radical_dvd_radical hdvd <| pow_ne_zero _ hb, radical_pow b hn, radical_dvd_self] + · classical + rwa [dvd_iff_normalizedFactors_le_normalizedFactors ha <| pow_ne_zero _ hb, + normalizedFactors_pow, Multiset.le_card_smul_iff_subset, ← Multiset.toFinset_subset, + toFinset_normalizedFactors, toFinset_normalizedFactors, + ← radical_dvd_iff_primeFactors_subset hb] + +theorem exists_dvd_radical_self_pow (ha : a ≠ 0) : ∃ n, a ∣ radical a ^ n := by + rw [exists_dvd_pow_iff_radical_dvd ha] + /-- Radical is multiplicative for relatively prime elements. -/ theorem radical_mul (hc : IsRelPrime a b) : radical (a * b) = radical a * radical b := by diff --git a/Mathlib/RingTheory/Radical/NatInt.lean b/Mathlib/RingTheory/Radical/NatInt.lean index bb14afc78a1ea0..fa1204627a638b 100644 --- a/Mathlib/RingTheory/Radical/NatInt.lean +++ b/Mathlib/RingTheory/Radical/NatInt.lean @@ -6,12 +6,10 @@ Authors: Bhavik Mehta, Arend Mellendijk, Jeremy Tan module public import Mathlib.Algebra.EuclideanDomain.Int -public import Mathlib.Algebra.GCDMonoid.Nat public import Mathlib.Data.Nat.Prime.Int -public import Mathlib.Data.Nat.PrimeFin +public import Mathlib.Data.Nat.Squarefree public import Mathlib.RingTheory.PrincipalIdealDomain public import Mathlib.RingTheory.Radical.Basic -public import Mathlib.RingTheory.UniqueFactorizationDomain.Nat /-! # The radical in `ℕ` and `ℤ` @@ -73,6 +71,16 @@ lemma radical_pos (n) : 0 < radical n := pos_of_ne_zero radical_ne_zero @[simp] lemma self_lt_radical_iff : n < radical n ↔ n = 0 := by simpa only [not_le, not_not] using radical_le_self_iff.not +theorem primeFactors_radical (n : ℕ) : (radical n).primeFactors = n.primeFactors := by + rw [radical_eq_prod_primeFactors, primeFactors_prod_primeFactors] + +theorem radical_dvd_iff {n k : ℕ} (hk : k ≠ 0) : + radical n ∣ k ↔ n.primeFactors ⊆ k.primeFactors := by + rw [radical_eq_prod_primeFactors, prod_primeFactors_dvd_iff hk] + +theorem dvd_radical_pow_self {n : ℕ} (hn : n ≠ 0) : n ∣ radical n ^ n := by + grw [radical_eq_prod_primeFactors, ← dvd_prod_primeFactors_pow_self hn] + open Qq Lean Mathlib.Meta Finset namespace Mathlib.Meta.Positivity diff --git a/Mathlib/RingTheory/RamificationInertia/Basic.lean b/Mathlib/RingTheory/RamificationInertia/Basic.lean index eba20d69cb7d25..1ff5e1a804766a 100644 --- a/Mathlib/RingTheory/RamificationInertia/Basic.lean +++ b/Mathlib/RingTheory/RamificationInertia/Basic.lean @@ -42,8 +42,8 @@ variable {R : Type*} [CommRing R] (p : Ideal R) [p.IsPrime] (S : Type*) [CommRin open IsLocalRing Module OrderIso PrimeSpectrum in theorem sum_ramification_inertia_eq_finrank_fiber - [Algebra.QuasiFinite R S] [Flat R S] [Fintype (p.primesOver S)] : - ∑ q : p.primesOver S, q.1.ramificationIdx' R * q.1.inertiaDeg' R = + [Algebra.QuasiFinite R S] [Fintype (p.primesOver S)] : + ∑ q : p.primesOver S, q.1.ramificationIdx R * q.1.inertiaDeg' R = finrank p.ResidueField (p.Fiber S) := by let := Fintype.ofFinite (PrimeSpectrum (p.Fiber S)) rw [IsArtinianRing.finrank_eq_sum_primeSpectrum, ← (primesOverOrderIsoFiber R S p).symm.sum_comp] @@ -52,7 +52,7 @@ theorem sum_ramification_inertia_eq_finrank_fiber simp_rw [toEquiv_symm, coe_symm_toEquiv, coe_primesOverOrderIsoFiber_symm_apply] set r := q.1.comap Algebra.TensorProduct.includeRight let := Localization.AtPrime.algebraOfLiesOver p r - rw [ramificationIdx'_eq p r, inertiaDeg'_eq p r] + rw [ramificationIdx_eq p r, inertiaDeg'_eq p r] let Rp := Localization.AtPrime p let Sq := Localization.AtPrime q.1 let Sr := Localization.AtPrime r @@ -71,7 +71,7 @@ ideal of `R`. Then the sum over all prime ideals `q` of `S` lying over `p` of th index of `q` times the inertia degree of `q` equals the rank of `S` as an `R`-module. -/ theorem sum_ramification_inertia_eq_finrank [IsDomain R] [Module.Finite R S] [Module.Flat R S] [Fintype (p.primesOver S)] : - ∑ q : p.primesOver S, q.1.ramificationIdx' R * q.1.inertiaDeg' R = Module.finrank R S := by + ∑ q : p.primesOver S, q.1.ramificationIdx R * q.1.inertiaDeg' R = Module.finrank R S := by rw [sum_ramification_inertia_eq_finrank_fiber, finrank_fiber_eq_finrank] /-- Let `S/R` be a finite flat extension of integral domains, and let `p` be prime ideal of `R`. @@ -81,7 +81,7 @@ degree of `q` equals the cardinality of `G`. -/ theorem sum_ramification_inertia_eq_card [IsDomain R] [IsDomain S] [Module.Finite R S] [Module.Flat R S] [Fintype (p.primesOver S)] {G : Type*} [Group G] [MulSemiringAction G S] [IsGaloisGroup G R S] : - ∑ q : p.primesOver S, q.1.ramificationIdx' R * q.1.inertiaDeg' R = Nat.card G := by + ∑ q : p.primesOver S, q.1.ramificationIdx R * q.1.inertiaDeg' R = Nat.card G := by let := IsGaloisGroup.finite G R S rw [sum_ramification_inertia_eq_finrank, IsGaloisGroup.card_eq_finrank' G R S] diff --git a/Mathlib/RingTheory/RamificationInertia/Inertia.lean b/Mathlib/RingTheory/RamificationInertia/Inertia.lean index b36614f2b52344..f01fb0e111881b 100644 --- a/Mathlib/RingTheory/RamificationInertia/Inertia.lean +++ b/Mathlib/RingTheory/RamificationInertia/Inertia.lean @@ -76,22 +76,36 @@ theorem inertiaDeg'_eq [q.LiesOver p] [q.IsPrime] [p.IsPrime] subst this exact inertiaDeg'_def q R -theorem inertiaDeg_eq_inertiaDeg' [q.LiesOver p] [p.IsMaximal] [q.IsMaximal] : - p.inertiaDeg q = q.inertiaDeg' R := by +theorem inertiaDeg'_eq_of_isFractionRing [q.LiesOver p] [p.IsPrime] [q.IsPrime] + (K L : Type*) [Field K] [Field L] + [Algebra (R ⧸ p) K] [IsFractionRing (R ⧸ p) K] + [Algebra (S ⧸ q) L] [IsFractionRing (S ⧸ q) L] + [Algebra R K] [IsScalarTower R (R ⧸ p) K] + [Algebra S L] [IsScalarTower S (S ⧸ q) L] + [Algebra R L] [IsScalarTower R S L] + [Algebra K L] [IsScalarTower R K L] : + q.inertiaDeg' R = Module.finrank K L := by + let := Localization.AtPrime.algebraOfLiesOver p q + rw [inertiaDeg'_eq p q] + apply Algebra.finrank_eq_of_equiv_equiv + (IsFractionRing.algEquivOfAlgEquiv (R := R) (A := R ⧸ p) (K := p.ResidueField) (L := K) .refl) + (IsFractionRing.algEquivOfAlgEquiv (R := S) (A := S ⧸ q) (K := q.ResidueField) (L := L) .refl) + apply IsFractionRing.ringHom_ext (A := R ⧸ p) + intro x + obtain ⟨x, rfl⟩ := Ideal.Quotient.mk_surjective x + simp [← IsScalarTower.algebraMap_apply R p.ResidueField q.ResidueField, + IsScalarTower.algebraMap_apply R S q.ResidueField, + ← IsScalarTower.algebraMap_apply R K L, ← IsScalarTower.algebraMap_apply R S L] + +theorem inertiaDeg'_eq_of_isMaximal [q.LiesOver p] [p.IsMaximal] [q.IsMaximal] : + q.inertiaDeg' R = Module.finrank (R ⧸ p) (S ⧸ q) := by let : Field (R ⧸ p) := Quotient.field p let : Field (S ⧸ q) := Quotient.field q - let := Localization.AtPrime.algebraOfLiesOver p q - rw [inertiaDeg'_eq p q, inertiaDeg_algebraMap] - let f := (algebraMap (S ⧸ q) q.ResidueField).comp (algebraMap (R ⧸ p) (S ⧸ q)) - let g := (algebraMap p.ResidueField q.ResidueField).comp (algebraMap (R ⧸ p) p.ResidueField) - have h : f = g := by ext; simp [f, g, ← IsScalarTower.algebraMap_apply] - let : Algebra (R ⧸ p) q.ResidueField := f.toAlgebra - have : IsScalarTower (R ⧸ p) (S ⧸ q) q.ResidueField := IsScalarTower.of_algebraMap_eq' rfl - have : IsScalarTower (R ⧸ p) p.ResidueField q.ResidueField := IsScalarTower.of_algebraMap_eq' h - rw [← mul_one (Module.finrank (R ⧸ p) (S ⧸ q)), - ← Module.finrank_of_bijective_algebraMap (bijective_algebraMap_quotient_residueField q), - Module.finrank_mul_finrank, ← Module.finrank_mul_finrank (R ⧸ p) p.ResidueField q.ResidueField, - Module.finrank_of_bijective_algebraMap (bijective_algebraMap_quotient_residueField p), one_mul] + exact inertiaDeg'_eq_of_isFractionRing p q (R ⧸ p) (S ⧸ q) + +theorem inertiaDeg_eq_inertiaDeg' [q.LiesOver p] [p.IsMaximal] [q.IsMaximal] : + p.inertiaDeg q = q.inertiaDeg' R := by + rw [inertiaDeg_algebraMap, inertiaDeg'_eq_of_isMaximal p q] theorem inertiaDeg'_tower [r.LiesOver q] : r.inertiaDeg' R = q.inertiaDeg' R * r.inertiaDeg' S := by @@ -105,6 +119,24 @@ theorem inertiaDeg'_tower [r.LiesOver q] : apply Module.finrank_mul_finrank · rw [inertiaDeg'_of_not_isPrime r R hr, inertiaDeg'_of_not_isPrime r S hr, mul_zero] +theorem inertiaDeg'_below_dvd [r.LiesOver q] : + q.inertiaDeg' R ∣ r.inertiaDeg' R := by + use r.inertiaDeg' S + rw [← inertiaDeg'_tower] + +theorem inertiaDeg'_above_dvd [r.LiesOver q] : + r.inertiaDeg' S ∣ r.inertiaDeg' R := by + use q.inertiaDeg' R + rw [mul_comm, ← inertiaDeg'_tower] + +theorem inertiaDeg'_below_le [r.IsPrime] [r.LiesOver q] [Module.Finite R T] : + q.inertiaDeg' R ≤ r.inertiaDeg' R := + Nat.le_of_dvd (r.inertiaDeg'_pos R) (q.inertiaDeg'_below_dvd r) + +theorem inertiaDeg'_above_le [r.IsPrime] [r.LiesOver q] [Module.Finite R T] : + r.inertiaDeg' S ≤ r.inertiaDeg' R := + Nat.le_of_dvd (r.inertiaDeg'_pos R) (q.inertiaDeg'_above_dvd r) + variable (R) in open Pointwise in @[simp] @@ -120,6 +152,33 @@ theorem inertiaDeg'_smul {G : Type*} [Group G] [MulSemiringAction G S] [SMulComm let e₂ := Ideal.residueFieldAlgEquiv' p (g • q) q f₀.symm (comap_symm f₀.toRingEquiv).symm exact e₂.toLinearEquiv.finrank_eq +theorem cardQuot_pow_inertiaDeg' [Module.Finite R S] [p.IsMaximal] [q.IsMaximal] [q.LiesOver p] : + p.cardQuot ^ q.inertiaDeg' R = q.cardQuot := by + let _ : Field (R ⧸ p) := Quotient.field p + rw [← inertiaDeg_eq_inertiaDeg' p q, inertiaDeg_algebraMap p q] + exact Module.natCard_eq_pow_finrank.symm + +theorem absNorm_pow_inertiaDeg' [Module.Finite R S] [q.IsPrime] [q.LiesOver p] + [IsDedekindDomain R] [IsDedekindDomain S] [Module.Free ℤ R] [Module.Free ℤ S] : + p.absNorm ^ q.inertiaDeg' R = q.absNorm := by + by_cases hp : p = ⊥ + · subst hp + simpa [eq_bot_of_liesOver_bot R q] using (inertiaDeg'_pos q R).ne' + have := isPrime_of_liesOver q p + have := isMaximal_of_isPrime_of_ne_bot p hp + have := IsMaximal.of_liesOver_isMaximal q p + exact cardQuot_pow_inertiaDeg' p q + +theorem natAbs_pow_inertiaDeg' [IsDedekindDomain R] [Module.Free ℤ R] [Module.Finite ℤ R] (p : ℤ) + (P : Ideal R) [P.IsPrime] [P.LiesOver (span {p})] : + p.natAbs ^ P.inertiaDeg' ℤ = absNorm P := by + simpa using absNorm_pow_inertiaDeg' (span {p}) P + +theorem pow_inertiaDeg' [IsDedekindDomain R] [Module.Free ℤ R] [Module.Finite ℤ R] (p : ℕ) + (P : Ideal R) [P.IsPrime] [P.LiesOver (span {(p : ℤ)})] : + p ^ P.inertiaDeg' ℤ = absNorm P := + natAbs_pow_inertiaDeg' p P + end end Ideal diff --git a/Mathlib/RingTheory/RamificationInertia/Ramification.lean b/Mathlib/RingTheory/RamificationInertia/Ramification.lean index 87cdea6fc27042..ffd80a1f9d4b0a 100644 --- a/Mathlib/RingTheory/RamificationInertia/Ramification.lean +++ b/Mathlib/RingTheory/RamificationInertia/Ramification.lean @@ -21,13 +21,13 @@ an `Sq`-module. ## Main definitions -* `Ideal.ramificationIdx' q R`: The ramification index of `q` over `R`. +* `Ideal.ramificationIdx q R`: The ramification index of `q` over `R`. ## Main statements -* `ramificationIdx_eq_ramificationIdx'`: The ramification index agrees with the usual definition in +* `ramificationIdx'_eq_ramificationIdx`: The ramification index agrees with the usual definition in the case of Dedekind domains. -* `ramificationIdx'_tower`: Ramification index is multiplicative in towers. +* `ramificationIdx_tower`: Ramification index is multiplicative in towers. -/ @@ -47,25 +47,30 @@ an `Sq`-module. When `q` is not prime, we use a junk value of `0`. -This will eventually replace the existing definition of `Ideal.ramificationIdx`. -/ -noncomputable def ramificationIdx' : ℕ := +This will eventually replace the existing definition of `Ideal.ramificationIdx'`. -/ +noncomputable def ramificationIdx : ℕ := if _ : q.IsPrime then letI Sq := Localization.AtPrime q (Module.length Sq (Sq ⧸ (q.under R).map (algebraMap R Sq))).toNat else 0 -theorem ramificationIdx'_def [q.IsPrime] : +theorem ramificationIdx_def [q.IsPrime] : letI Sq := Localization.AtPrime q - q.ramificationIdx' R = (Module.length Sq (Sq ⧸ (q.under R).map (algebraMap R Sq))).toNat := + q.ramificationIdx R = (Module.length Sq (Sq ⧸ (q.under R).map (algebraMap R Sq))).toNat := dif_pos _ -theorem ramificationIdx'_of_not_isPrime (hq : ¬ q.IsPrime) : q.ramificationIdx' R = 0 := +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_def := ramificationIdx_def + +theorem ramificationIdx_of_not_isPrime (hq : ¬ q.IsPrime) : q.ramificationIdx R = 0 := dif_neg hq -theorem ramificationIdx'_pos [q.IsPrime] [Module.Finite R S] : 0 < q.ramificationIdx' R := by +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_of_not_isPrime := + ramificationIdx_of_not_isPrime + +theorem ramificationIdx_pos [q.IsPrime] [Module.Finite R S] : 0 < q.ramificationIdx R := by let p := q.under R let Sq := Localization.AtPrime q - rw [ramificationIdx'_def] + rw [ramificationIdx_def] apply ENat.toNat_pos · rw [← pos_iff_ne_zero, Module.length_pos_iff, Submodule.Quotient.nontrivial_iff, IsScalarTower.algebraMap_eq R S, ← map_map, ← lt_top_iff_ne_top] @@ -80,23 +85,28 @@ theorem ramificationIdx'_pos [q.IsPrime] [Module.Finite R S] : 0 < q.ramificatio rwa [Module.length_eq_of_surjective (R := Sq ⧸ p.map (algebraMap R Sq)) Quotient.mk_surjective, Module.length_ne_top_iff, ← isArtinianRing_iff_isFiniteLength] -theorem ramificationIdx'_eq_one [q.IsPrime] [Algebra.EssFiniteType R S] - [Algebra.IsUnramifiedAt R q] : q.ramificationIdx' R = 1 := by +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_pos := ramificationIdx_pos + +theorem ramificationIdx_eq_one [q.IsPrime] [Algebra.EssFiniteType R S] + [Algebra.IsUnramifiedAt R q] : q.ramificationIdx R = 1 := by let p := q.under R let Rp := Localization.AtPrime p let Sq := Localization.AtPrime q let : Algebra Rp Sq := Localization.AtPrime.algebraOfLiesOver p q have : Algebra.EssFiniteType Rp Sq := Algebra.EssFiniteType.of_comp R Rp Sq - rw [ramificationIdx'_def, ENat.toNat_eq_iff_eq_coe, Nat.cast_one, Module.length_eq_one_iff, + rw [ramificationIdx_def, ENat.toNat_eq_iff_eq_coe, Nat.cast_one, Module.length_eq_one_iff, isSimpleModule_iff_isCoatom, ← Ideal.isMaximal_def, IsLocalRing.isMaximal_iff, IsScalarTower.algebraMap_eq R Rp Sq, ← map_map, Localization.AtPrime.map_eq_maximalIdeal] exact Algebra.FormallyUnramified.map_maximalIdeal -theorem ramificationIdx'_eq_one_iff [q.IsPrime] [Algebra.EssFiniteType R S] +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_eq_one := ramificationIdx_eq_one + +variable {q R} in +theorem ramificationIdx_eq_one_iff [q.IsPrime] [Algebra.EssFiniteType R S] [Algebra.IsIntegral R S] [PerfectField (q.under R).ResidueField] : - q.ramificationIdx' R = 1 ↔ Algebra.IsUnramifiedAt R q := by - refine ⟨fun h ↦ ?_, fun _ ↦ ramificationIdx'_eq_one q R⟩ - rw [ramificationIdx'_def, ENat.toNat_eq_iff_eq_coe, Nat.cast_one, Module.length_eq_one_iff, + q.ramificationIdx R = 1 ↔ Algebra.IsUnramifiedAt R q := by + refine ⟨fun h ↦ ?_, fun _ ↦ ramificationIdx_eq_one q R⟩ + rw [ramificationIdx_def, ENat.toNat_eq_iff_eq_coe, Nat.cast_one, Module.length_eq_one_iff, isSimpleModule_iff_isCoatom, ← Ideal.isMaximal_def, IsLocalRing.isMaximal_iff] at h let p := q.under R let Rp := Localization.AtPrime p @@ -108,6 +118,9 @@ theorem ramificationIdx'_eq_one_iff [q.IsPrime] [Algebra.EssFiniteType R S] ← Localization.AtPrime.map_eq_maximalIdeal, map_map, ← IsScalarTower.algebraMap_eq] exact ⟨Algebra.IsAlgebraic.isSeparable_of_perfectField, h⟩ +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_eq_one_iff := + ramificationIdx_eq_one_iff + end section @@ -116,63 +129,136 @@ variable {R S T : Type*} [CommRing R] [CommRing S] [CommRing T] [Algebra R S] [Algebra R T] [Algebra S T] [IsScalarTower R S T] (p : Ideal R) (q : Ideal S) (r : Ideal T) -theorem ramificationIdx'_eq [q.LiesOver p] [q.IsPrime] : +theorem ramificationIdx_eq [q.LiesOver p] [q.IsPrime] : letI Sq := Localization.AtPrime q - q.ramificationIdx' R = (Module.length Sq (Sq ⧸ p.map (algebraMap R Sq))).toNat := by - rw [ramificationIdx'_def, over_def q p] + q.ramificationIdx R = (Module.length Sq (Sq ⧸ p.map (algebraMap R Sq))).toNat := by + rw [ramificationIdx_def, over_def q p] + +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_eq := ramificationIdx_eq open Localization IsLocalization.AtPrime in -theorem ramificationIdx_eq_ramificationIdx' - [IsDomain R] [IsDedekindDomain S] [Module.IsTorsionFree R S] - [q.LiesOver p] [hq : q.IsPrime] (hp : p ≠ ⊥) : - p.ramificationIdx q = q.ramificationIdx' R := by - have : p.IsPrime := isPrime_of_liesOver q p - have hq' : q ≠ ⊥ := ne_bot_of_liesOver_of_ne_bot hp q +theorem ramificationIdx'_eq_ramificationIdx' [IsDedekindDomain S] + [q.LiesOver p] [hq : q.IsPrime] (hpS : p.map (algebraMap R S) ≠ ⊥) : + p.ramificationIdx' q = q.ramificationIdx R := by + have hq' : q ≠ ⊥ := ne_bot_of_le_ne_bot hpS (map_le_of_le_comap (q.over_def p).le) have : q.IsMaximal := hq.isMaximal hq' - have hpS : p.map (algebraMap R S) ≠ ⊥ := map_ne_bot_of_ne_bot hp obtain ⟨I, hqI, h⟩ := Ideal.eq_prime_pow_mul_coprime hpS q replace hqI : ¬ I ≤ q := by contrapose! hqI rw [sup_of_le_left hqI] exact hq.ne_top - rw [← IsDedekindDomain.ramificationIdx_eq_normalizedFactors_count hpS hq hq'] at h + rw [← IsDedekindDomain.ramificationIdx'_eq_normalizedFactors_count hpS hq hq'] at h apply_fun (map (algebraMap S (Localization.AtPrime q))) at h rw [map_map, ← IsScalarTower.algebraMap_eq, Ideal.map_mul, Ideal.map_pow, map_eq_top_of_not_le (Localization.AtPrime q) hqI, mul_top, AtPrime.map_eq_maximalIdeal] at h have hSq := isDiscreteValuationRing_of_dedekind_domain S hq' (Localization.AtPrime q) - rw [ramificationIdx'_eq p q, h, hSq.length_quotient_pow_maximalIdeal, ENat.toNat_coe] + rw [ramificationIdx_eq p q, h, hSq.length_quotient_pow_maximalIdeal, ENat.toNat_coe] + +@[deprecated (since := "2026-07-01")] alias ramificationIdx_eq_ramificationIdx'' := + ramificationIdx'_eq_ramificationIdx' + +theorem ramificationIdx'_eq_ramificationIdx [IsDomain R] [IsDedekindDomain S] + [Module.IsTorsionFree R S] [q.LiesOver p] [hq : q.IsPrime] (hp : p ≠ ⊥) : + p.ramificationIdx' q = q.ramificationIdx R := by + have hpS : p.map (algebraMap R S) ≠ ⊥ := map_ne_bot_of_ne_bot hp + exact ramificationIdx'_eq_ramificationIdx' p q hpS + +@[deprecated (since := "2026-07-01")] alias ramificationIdx_eq_ramificationIdx' := + ramificationIdx'_eq_ramificationIdx + +namespace IsDedekindDomain + +open UniqueFactorizationMonoid + +theorem ramificationIdx_eq_factors_count [IsDedekindDomain S] + [q.LiesOver p] (hp0 : p.map (algebraMap R S) ≠ ⊥) : + q.ramificationIdx R = (factors (p.map (algebraMap R S))).count q := by + by_cases hq : q.IsPrime; swap + · rw [ramificationIdx_of_not_isPrime q R hq, eq_comm, Multiset.count_eq_zero] + contrapose! hq + exact isPrime_of_prime (prime_of_factor q hq) + have hq0 : q ≠ ⊥ := ne_bot_of_le_ne_bot hp0 (map_le_of_le_comap (q.over_def p).le) + rw [← ramificationIdx'_eq_ramificationIdx' p q hp0, ramificationIdx'_eq_factors_count hp0 ‹_› hq0] -/-- See `ramificationIdx'_tower` for a version that does not assume primality. -/ -theorem ramificationIdx'_tower' [q.IsPrime] [r.IsPrime] [r.LiesOver q] +open UniqueFactorizationMonoid in +theorem ramificationIdx_eq_normalizedFactors_count [IsDedekindDomain S] + [q.LiesOver p] (hp0 : p.map (algebraMap R S) ≠ ⊥) : + q.ramificationIdx R = (normalizedFactors (p.map (algebraMap R S))).count q := by + rw [← factors_eq_normalizedFactors, ← ramificationIdx_eq_factors_count p q hp0] + +open UniqueFactorizationMonoid in +theorem ramificationIdx_eq_multiplicity [IsDedekindDomain S] + [q.IsPrime] [q.LiesOver p] (hp : p.map (algebraMap R S) ≠ ⊥) : + q.ramificationIdx R = multiplicity q (p.map (algebraMap R S)) := by + have hq : q ≠ ⊥ := ne_bot_of_le_ne_bot hp (map_le_of_le_comap (q.over_def p).le) + rw [ramificationIdx_eq_normalizedFactors_count p q hp, + multiplicity_eq_of_emultiplicity_eq_some (emultiplicity_eq_count_normalizedFactors + (prime_of_isPrime hq inferInstance).irreducible hp), normalize_eq] + +end IsDedekindDomain + +/-- See `ramificationIdx_tower` for a version that does not assume primality. -/ +theorem ramificationIdx_tower' [q.IsPrime] [r.IsPrime] [r.LiesOver q] [Algebra (Localization.AtPrime q) (Localization.AtPrime r)] [Localization.AtPrime.IsLiesOverAlgebra q r] [Module.Flat (Localization.AtPrime q) (Localization.AtPrime r)] : - r.ramificationIdx' R = q.ramificationIdx' R * r.ramificationIdx' S := by + r.ramificationIdx R = q.ramificationIdx R * r.ramificationIdx S := by have : q.LiesOver (r.under R) := LiesOver.tower_bot r q (r.under R) let f := (Ideal.quotientEquivAlgOfEq (Localization.AtPrime r) (by rw [map_map, ← IsScalarTower.algebraMap_eq])).trans (Algebra.TensorProduct.quotIdealMapEquivTensorQuot (Localization.AtPrime r) ((r.under R).map (algebraMap R (Localization.AtPrime q)))) - rw [ramificationIdx'_def, ramificationIdx'_eq (r.under R), ramificationIdx'_eq q, + rw [ramificationIdx_def, ramificationIdx_eq (r.under R), ramificationIdx_eq q, f.toLinearEquiv.length_eq, IsLocalRing.length_baseChange, ENat.toNat_mul, ← Localization.AtPrime.map_eq_maximalIdeal, map_map, ← IsScalarTower.algebraMap_eq] -/-- See `ramificationIdx'_tower'` for a version that only assumes local flatness. -/ -theorem ramificationIdx'_tower [r.LiesOver q] [Module.Flat S T] : - r.ramificationIdx' R = q.ramificationIdx' R * r.ramificationIdx' S := by +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_tower' := ramificationIdx_tower' + +/-- See `ramificationIdx_tower'` for a version that only assumes local flatness. -/ +theorem ramificationIdx_tower [r.LiesOver q] [Module.Flat S T] : + r.ramificationIdx R = q.ramificationIdx R * r.ramificationIdx S := by by_cases hr : r.IsPrime · have : q.IsPrime := isPrime_of_liesOver r q let := Localization.AtPrime.algebraOfLiesOver q r - apply ramificationIdx'_tower' - · rw [ramificationIdx'_of_not_isPrime r R hr, ramificationIdx'_of_not_isPrime r S hr, mul_zero] + apply ramificationIdx_tower' + · rw [ramificationIdx_of_not_isPrime r R hr, ramificationIdx_of_not_isPrime r S hr, mul_zero] + +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_tower := ramificationIdx_tower + +theorem ramificationIdx_below_dvd [r.LiesOver q] [Module.Flat S T] : + q.ramificationIdx R ∣ r.ramificationIdx R := by + use r.ramificationIdx S + rw [← ramificationIdx_tower] + +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_below_dvd := ramificationIdx_below_dvd + +theorem ramificationIdx_above_dvd [r.LiesOver q] [Module.Flat S T] : + r.ramificationIdx S ∣ r.ramificationIdx R := by + use q.ramificationIdx R + rw [mul_comm, ← ramificationIdx_tower] + +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_above_dvd := ramificationIdx_above_dvd + +theorem ramificationIdx_below_le [r.IsPrime] [r.LiesOver q] [Module.Finite R T] [Module.Flat S T] : + q.ramificationIdx R ≤ r.ramificationIdx R := + Nat.le_of_dvd (r.ramificationIdx_pos R) (q.ramificationIdx_below_dvd r) + +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_below_le := + ramificationIdx_below_le + +theorem ramificationIdx_above_le [r.IsPrime] [r.LiesOver q] [Module.Finite R T] [Module.Flat S T] : + r.ramificationIdx S ≤ r.ramificationIdx R := + Nat.le_of_dvd (r.ramificationIdx_pos R) (q.ramificationIdx_above_dvd r) + +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_above_le := ramificationIdx_above_le variable (R) in open Pointwise in @[simp] -theorem ramificationIdx'_smul {G : Type*} [Group G] [MulSemiringAction G S] [SMulCommClass G R S] - (g : G) : (g • q).ramificationIdx' R = q.ramificationIdx' R := by +theorem ramificationIdx_smul {G : Type*} [Group G] [MulSemiringAction G S] [SMulCommClass G R S] + (g : G) : (g • q).ramificationIdx R = q.ramificationIdx R := by by_cases hq : q.IsPrime; swap - · rw [ramificationIdx'_of_not_isPrime, ramificationIdx'_of_not_isPrime] <;> simpa + · rw [ramificationIdx_of_not_isPrime, ramificationIdx_of_not_isPrime] <;> simpa · let p := q.under R let f₀ := MulSemiringAction.toAlgAut G R S g have hg : g • q = q.map f₀ := q.pointwise_smul_def @@ -186,9 +272,11 @@ theorem ramificationIdx'_smul {G : Type*} [Group G] [MulSemiringAction G S] [SMu Ideal.quotientEquivAlg _ _ (AlgEquiv.ofBijective (Algebra.ofId Sq Sq') f.bijective) (by rw [IsScalarTower.algebraMap_eq R Sq Sq', Ideal.map_map, ← AlgEquiv.toAlgHom_toRingHom, AlgEquiv.toAlgHom_ofBijective, Algebra.toRingHom_ofId]) - rw [hg, ramificationIdx'_eq p q, ramificationIdx'_eq p (q.map f₀), + rw [hg, ramificationIdx_eq p q, ramificationIdx_eq p (q.map f₀), e.toLinearEquiv.length_eq, Module.length_eq_of_surjective f.surjective] +@[deprecated (since := "2026-07-01")] alias ramificationIdx'_smul := ramificationIdx_smul + end end Ideal diff --git a/Mathlib/RingTheory/RingHom/QuasiFinite.lean b/Mathlib/RingTheory/RingHom/QuasiFinite.lean index da49a14fd38f9d..0d711883d8668b 100644 --- a/Mathlib/RingTheory/RingHom/QuasiFinite.lean +++ b/Mathlib/RingTheory/RingHom/QuasiFinite.lean @@ -81,7 +81,7 @@ lemma QuasiFinite.ofLocalizationSpanTarget : OfLocalizationSpanTarget QuasiFinit let φ (r : s) : P.Fiber S →ₐ[P.ResidueField] P.Fiber (Localization.Away r.1) := Algebra.TensorProduct.map (.id _ _) (IsScalarTower.toAlgHom _ _ _) let f : P.Fiber S →ₐ[P.ResidueField] Π r : s, (P.Fiber (Localization.Away r.1)) := - Pi.algHom _ _ φ + AlgHom.pi φ have : IsNoetherian P.ResidueField (Π r : s, (P.Fiber (Localization.Away r.1))) := isNoetherian_of_isNoetherianRing_of_finite .. suffices Function.Injective f from .of_injective f.toLinearMap this diff --git a/Mathlib/RingTheory/RootsOfUnity/Basic.lean b/Mathlib/RingTheory/RootsOfUnity/Basic.lean index df52d96eddf247..cd6d861a10e498 100644 --- a/Mathlib/RingTheory/RootsOfUnity/Basic.lean +++ b/Mathlib/RingTheory/RootsOfUnity/Basic.lean @@ -242,18 +242,19 @@ theorem rootsOfUnityEquivNthRoots_symm_apply (x : { x // x ∈ nthRoots k (1 : R variable (k R) -instance rootsOfUnity.fintype : Fintype (rootsOfUnity k R) := by +instance : Finite (rootsOfUnity k R) := by classical - exact Fintype.ofEquiv { x // x ∈ nthRoots k (1 : R) } (rootsOfUnityEquivNthRoots R k).symm + exact .of_equiv { x // x ∈ nthRoots k (1 : R) } (rootsOfUnityEquivNthRoots R k).symm instance rootsOfUnity.isCyclic : IsCyclic (rootsOfUnity k R) := isCyclic_of_injective_ringHom ((Units.coeHom R).comp (rootsOfUnity k R).subtype) coe_injective -theorem card_rootsOfUnity : Fintype.card (rootsOfUnity k R) ≤ k := by +theorem card_rootsOfUnity : Nat.card (rootsOfUnity k R) ≤ k := by classical calc - Fintype.card (rootsOfUnity k R) = Fintype.card { x // x ∈ nthRoots k (1 : R) } := - Fintype.card_congr (rootsOfUnityEquivNthRoots R k) + Nat.card (rootsOfUnity k R) = Nat.card { x // x ∈ nthRoots k (1 : R) } := + Nat.card_congr (rootsOfUnityEquivNthRoots R k) + _ = Fintype.card { x // x ∈ nthRoots k (1 : R) } := Nat.card_eq_fintype_card _ ≤ Multiset.card (nthRoots k (1 : R)).attach := Multiset.card_le_card (Multiset.dedup_le _) _ = Multiset.card (nthRoots k (1 : R)) := Multiset.card_attach _ ≤ k := card_nthRoots k 1 diff --git a/Mathlib/RingTheory/RootsOfUnity/Complex.lean b/Mathlib/RingTheory/RootsOfUnity/Complex.lean index f982dac2e96d73..aec808d1ed3b3b 100644 --- a/Mathlib/RingTheory/RootsOfUnity/Complex.lean +++ b/Mathlib/RingTheory/RootsOfUnity/Complex.lean @@ -119,7 +119,7 @@ nonrec theorem mem_rootsOfUnity (n : ℕ) [NeZero n] (x : Units ℂ) : use i simp [field] -theorem card_rootsOfUnity (n : ℕ) [NeZero n] : Fintype.card (rootsOfUnity n ℂ) = n := +theorem card_rootsOfUnity (n : ℕ) [NeZero n] : Nat.card (rootsOfUnity n ℂ) = n := (isPrimitiveRoot_exp n NeZero.out).card_rootsOfUnity theorem card_primitiveRoots (k : ℕ) : (primitiveRoots k ℂ).card = φ k := by diff --git a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean index 04235887b02e7e..2b2c98494c996d 100644 --- a/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean +++ b/Mathlib/RingTheory/RootsOfUnity/CyclotomicUnits.lean @@ -39,6 +39,20 @@ variable {n i j p : ℕ} {A K : Type*} {ζ : A} variable [CommRing A] [IsDomain A] {R : Type*} [CommRing R] [Algebra R A] +/-- If `ζ ^ n = 1` and `ζ ≠ 1`, then `ζ - 1` divides `n`. This does not require `ζ` to be a + primitive root of unity, only a root of unity different from `1`. -/ +theorem sub_one_dvd_natCast_of_pow_eq_one (hζ : ζ ^ n = 1) (hζ1 : ζ ≠ 1) : ζ - 1 ∣ (n : A) := by + have key : (n : A) = ∑ i ∈ range n, (1 - ζ ^ i) := by + have hgs : ∑ i ∈ range n, ζ ^ i = 0 := by + have := geom_sum_mul ζ n + rw [hζ, sub_self] at this + exact (mul_eq_zero.1 this).resolve_right fun h ↦ hζ1 (sub_eq_zero.1 h) + rw [Finset.sum_sub_distrib, hgs, sub_zero, Finset.sum_const, card_range, nsmul_eq_mul, mul_one] + rw [key] + refine Finset.dvd_sum fun i _ ↦ ?_ + have h : ζ - 1 ∣ ζ ^ i - 1 := by simpa using sub_dvd_pow_sub_pow ζ 1 i + rwa [← dvd_neg, neg_sub] at h + namespace IsPrimitiveRoot /-- Given an `n`-th primitive root of unity `ζ,` we have that `ζ - 1` and `ζ ^ j - 1` are associated @@ -118,15 +132,13 @@ theorem associated_pow_add_sub_sub_one (hζ : IsPrimitiveRoot ζ n) (hn : 2 ≤ /-- If `p` is prime and `ζ` is a `p`-th primitive root of unity, then `ζ - 1` and `η₁ - η₂` are associated for all distinct `p`-th roots of unity `η₁` and `η₂`. -/ -lemma ntRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitiveRoot ζ p) +lemma nthRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitiveRoot ζ p) (hp : p.Prime) : - Set.Pairwise (nthRootsFinset p (1 : A)) (fun η₁ η₂ ↦ Associated (ζ - 1) (η₁ - η₂)) := by + Set.Pairwise (nthRootsFinset p (1 : A)) fun η₁ η₂ ↦ Associated (ζ - 1) (η₁ - η₂) := by intro η₁ hη₁ η₂ hη₂ e have : NeZero p := ⟨hp.ne_zero⟩ - obtain ⟨i, hi, rfl⟩ := - hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₁) - obtain ⟨j, hj, rfl⟩ := - hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₂) + obtain ⟨i, hi, rfl⟩ := hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₁) + obtain ⟨j, hj, rfl⟩ := hζ.eq_pow_of_pow_eq_one ((Polynomial.mem_nthRootsFinset hp.pos 1).1 hη₂) wlog hij : j ≤ i · simpa using (this hζ ‹_› ‹_› _ hj ‹_› _ hi ‹_› e.symm (by lia)).neg_right have H : (i - j).Coprime p := (coprime_of_lt_prime (by grind) (by grind) hp).symm @@ -134,4 +146,24 @@ lemma ntRootsFinset_pairwise_associated_sub_one_sub_of_prime (hζ : IsPrimitiveR simp only [hij, add_tsub_cancel_of_le] at h rw [← h, associated_mul_unit_right_iff] +@[deprecated (since := "2026-06-23")] +alias ntRootsFinset_pairwise_associated_sub_one_sub_of_prime := + nthRootsFinset_pairwise_associated_sub_one_sub_of_prime + +/-- If `p` is prime and `ζ` is a `p`-th primitive root of unity, then `ζ - 1` divides `η₁ - η₂` +for all `p`-th roots of unity `η₁` and `η₂`. -/ +lemma sub_one_dvd_sub (hζ : IsPrimitiveRoot ζ p) (hp : p.Prime) + {η₁ : A} (hη₁ : η₁ ∈ nthRootsFinset p (1 : A)) + {η₂ : A} (hη₂ : η₂ ∈ nthRootsFinset p (1 : A)) : + ζ - 1 ∣ η₁ - η₂ := by + rcases eq_or_ne η₁ η₂ with rfl | h + · simp + · exact (hζ.nthRootsFinset_pairwise_associated_sub_one_sub_of_prime hp hη₁ hη₂ h).dvd + +/-- Given an `n`-th primitive root of unity `ζ`, where `1 < n`, we have that `ζ - 1` divides `n`. + In particular, if `ζ` is a `p`-th primitive root of unity with `p` prime, then `ζ - 1` divides + `p`. -/ +theorem sub_one_dvd_natCast (hζ : IsPrimitiveRoot ζ n) (hn : 1 < n) : ζ - 1 ∣ (n : A) := + sub_one_dvd_natCast_of_pow_eq_one hζ.pow_eq_one (hζ.ne_one hn) + end IsPrimitiveRoot diff --git a/Mathlib/RingTheory/RootsOfUnity/EnoughRootsOfUnity.lean b/Mathlib/RingTheory/RootsOfUnity/EnoughRootsOfUnity.lean index 2def918eb743b2..d9b3704e3ad231 100644 --- a/Mathlib/RingTheory/RootsOfUnity/EnoughRootsOfUnity.lean +++ b/Mathlib/RingTheory/RootsOfUnity/EnoughRootsOfUnity.lean @@ -80,7 +80,7 @@ lemma natCard_rootsOfUnity (M : Type*) [CommMonoid M] (n : ℕ) [NeZero n] rw [← Units.val_inj, Units.val_pow_eq_pow_val, IsUnit.unit_spec, h.pow_eq_one, Units.val_one] lemma of_card_le {R : Type*} [CommRing R] [IsDomain R] {n : ℕ} [NeZero n] - (h : n ≤ Fintype.card (rootsOfUnity n R)) : HasEnoughRootsOfUnity R n where + (h : n ≤ Nat.card (rootsOfUnity n R)) : HasEnoughRootsOfUnity R n where prim := card_rootsOfUnity_eq_iff_exists_isPrimitiveRoot.mp (le_antisymm (card_rootsOfUnity R n) h) cyc := rootsOfUnity.isCyclic R n @@ -104,7 +104,6 @@ group of units of a ring `M` with all roots of unity is isomorphic to `G` -/ lemma IsCyclic.monoidHom_equiv_self (G M : Type*) [CommGroup G] [Finite G] [IsCyclic G] [CommMonoid M] [HasEnoughRootsOfUnity M (Nat.card G)] : Nonempty ((G →* Mˣ) ≃* G) := by - have : NeZero (Nat.card G) := ⟨Nat.card_pos.ne'⟩ have hord := HasEnoughRootsOfUnity.natCard_rootsOfUnity M (Nat.card G) let e := (IsCyclic.monoidHom_mulEquiv_rootsOfUnity G Mˣ).some exact ⟨e.trans (rootsOfUnityUnitsMulEquiv M (Nat.card G)) |>.trans (mulEquivOfCyclicCardEq hord)⟩ diff --git a/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean b/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean index 783f533a4c74fe..7195f3618a935e 100644 --- a/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean +++ b/Mathlib/RingTheory/RootsOfUnity/PrimitiveRoots.lean @@ -33,6 +33,8 @@ monoids, expressing that an element is a primitive root of unity. has a primitive `k`-th root of unity, then it has `φ k` of them. * `primitiveRootsPowEquivOfCoprime`: An equivalence between `primitiveRoots k R` that takes each root to a coprime power `a`. +* `nthRootsFinset_eq_of_prime`: for `p` prime, the `p`-th roots of unity are the primitive `p`-th + roots of unity together with `1`. ## Implementation details @@ -154,6 +156,10 @@ theorem one_right_iff : IsPrimitiveRoot ζ 1 ↔ ζ = 1 := by · intro h; rw [← pow_one ζ, h.pow_eq_one] · rintro rfl; exact one +@[simp] +theorem one_left_iff : IsPrimitiveRoot (1 : M) k ↔ k = 1 := + ⟨fun h ↦ Nat.dvd_one.mp (h.dvd_of_pow_eq_one 1 (one_pow _)), fun e ↦ e ▸ one⟩ + @[simp] theorem coe_submonoidClass_iff {M B : Type*} [CommMonoid M] [SetLike B M] [SubmonoidClass B M] {N : B} {ζ : N} : IsPrimitiveRoot (ζ : M) k ↔ IsPrimitiveRoot ζ k := by @@ -240,6 +246,12 @@ theorem pow_of_dvd (h : IsPrimitiveRoot ζ k) {p : ℕ} (hp : p ≠ 0) (hdiv : p rw [← orderOf_pow_of_dvd hp hdiv] exact IsPrimitiveRoot.orderOf _ +/-- If `ζ` is a primitive `k`-th root of unity with `k` odd, then every power of `ζ` is an even +power of `ζ`. -/ +theorem exists_pow_eq_pow_two_mul (h : IsPrimitiveRoot ζ k) (hk : Odd k) (n : ℕ) : + ∃ m, ζ ^ n = ζ ^ (2 * m) := + _root_.exists_pow_eq_pow_two_mul h.pow_eq_one hk n + protected theorem mem_rootsOfUnity {ζ : Mˣ} {n : ℕ} (h : IsPrimitiveRoot ζ n) : ζ ∈ rootsOfUnity n M := h.pow_eq_one @@ -502,15 +514,11 @@ variable [IsDomain R] theorem zpowers_eq {k : ℕ} [NeZero k] {ζ : Rˣ} (h : IsPrimitiveRoot ζ k) : Subgroup.zpowers ζ = rootsOfUnity k R := by - apply SetLike.coe_injective - have F : Fintype (Subgroup.zpowers ζ) := Fintype.ofEquiv _ h.zmodEquivZPowers.toEquiv - refine - @Set.eq_of_subset_of_card_le Rˣ _ _ F (rootsOfUnity.fintype R k) - (Subgroup.zpowers_le_of_mem <| show ζ ∈ rootsOfUnity k R from h.pow_eq_one) ?_ + apply Subgroup.eq_of_le_of_card_ge (Subgroup.zpowers_le_of_mem h.pow_eq_one) calc - Fintype.card (rootsOfUnity k R) ≤ k := card_rootsOfUnity R k - _ = Fintype.card (ZMod k) := (ZMod.card k).symm - _ = Fintype.card (Subgroup.zpowers ζ) := Fintype.card_congr h.zmodEquivZPowers.toEquiv + Nat.card (rootsOfUnity k R) ≤ k := card_rootsOfUnity R k + _ = Nat.card (ZMod k) := (Nat.card_zmod k).symm + _ = Nat.card (Subgroup.zpowers ζ) := Nat.card_congr h.zmodEquivZPowers.toEquiv lemma map_rootsOfUnity {S F} [CommRing S] [IsDomain S] [FunLike F R S] [MonoidHomClass F R S] {ζ : R} {n : ℕ} [NeZero n] (hζ : IsPrimitiveRoot ζ n) {f : F} (hf : Function.Injective f) : @@ -634,26 +642,20 @@ theorem card_nthRoots {n : ℕ} {ζ : R} (hζ : IsPrimitiveRoot ζ n) (a : R) : /-- A variant of `IsPrimitiveRoot.card_rootsOfUnity` for `ζ : Rˣ`. -/ theorem card_rootsOfUnity' {n : ℕ} [NeZero n] (h : IsPrimitiveRoot ζ n) : - Fintype.card (rootsOfUnity n R) = n := by - let e := h.zmodEquivZPowers - have : Fintype (Subgroup.zpowers ζ) := Fintype.ofEquiv _ e.toEquiv - calc - Fintype.card (rootsOfUnity n R) = Fintype.card (Subgroup.zpowers ζ) := - Fintype.card_congr <| by rw [h.zpowers_eq] - _ = Fintype.card (ZMod n) := Fintype.card_congr e.toEquiv.symm - _ = n := ZMod.card n + Nat.card (rootsOfUnity n R) = n := by + rw [← h.zpowers_eq, Nat.card_zpowers, h.eq_orderOf] theorem card_rootsOfUnity {ζ : R} {n : ℕ} [NeZero n] (h : IsPrimitiveRoot ζ n) : - Fintype.card (rootsOfUnity n R) = n := by + Nat.card (rootsOfUnity n R) = n := by obtain ⟨ζ, hζ⟩ := h.isUnit NeZero.out rw [← hζ, IsPrimitiveRoot.coe_units_iff] at h exact h.card_rootsOfUnity' lemma _root_.card_rootsOfUnity_eq_iff_exists_isPrimitiveRoot {n : ℕ} [NeZero n] : - Fintype.card (rootsOfUnity n R) = n ↔ ∃ ζ : R, IsPrimitiveRoot ζ n := by + Nat.card (rootsOfUnity n R) = n ↔ ∃ ζ : R, IsPrimitiveRoot ζ n := by refine ⟨fun h ↦ ?_, fun ⟨ζ, hζ⟩ ↦ hζ.card_rootsOfUnity⟩ obtain ⟨⟨ζ, hζ'⟩, hζ⟩ := (rootsOfUnity.isCyclic R n).exists_ofOrder_eq_natCard - rw [Nat.card_eq_fintype_card, h, ← IsPrimitiveRoot.iff_orderOf, ← coe_submonoidClass_iff, + rw [h, ← IsPrimitiveRoot.iff_orderOf, ← coe_submonoidClass_iff, ← IsPrimitiveRoot.coe_units_iff] at hζ use ζ @@ -830,6 +832,37 @@ end Automorphisms end IsPrimitiveRoot +section nthRootsFinsetPrime + +open IsPrimitiveRoot + +variable [CommRing R] [IsDomain R] {p : ℕ} + +/-- If `p` is prime, the `p`-th roots of unity in an integral domain are exactly the primitive +`p`-th roots of unity together with `1`. -/ +theorem nthRootsFinset_eq_of_prime [DecidableEq R] (hp : p.Prime) : + nthRootsFinset p (1 : R) = primitiveRoots p R ∪ {1} := by + simp [nthRoots_one_eq_biUnion_primitiveRoots, hp.divisors] + +/-- For `p` prime, an element of `R` is a `p`-th root of unity if and only if it is either a +primitive `p`-th root of unity or `1`. -/ +theorem mem_nthRootsFinset_iff_of_prime (hp : p.Prime) {η : R} : + η ∈ nthRootsFinset p (1 : R) ↔ IsPrimitiveRoot η p ∨ η = 1 := by + classical + simp [nthRootsFinset_eq_of_prime hp, mem_primitiveRoots hp.pos, or_comm] + +/-- A `p`-th root of unity that is not `1`, with `p` prime, satisfies `IsPrimitiveRoot η p`. -/ +theorem isPrimitiveRoot_of_mem_nthRootsFinset (hp : p.Prime) {η : R} + (hη : η ∈ nthRootsFinset p (1 : R)) (hne1 : η ≠ 1) : IsPrimitiveRoot η p := + ((mem_nthRootsFinset_iff_of_prime hp).1 hη).resolve_right hne1 + +/-- A `p`-th root of unity that is not `1`, with `p` prime, is a primitive `p`-th root of unity. -/ +theorem mem_primitiveRoots_of_mem_nthRootsFinset (hp : p.Prime) {η : R} + (hη : η ∈ nthRootsFinset p (1 : R)) (hne1 : η ≠ 1) : η ∈ primitiveRoots p R := + (mem_primitiveRoots hp.pos).2 (isPrimitiveRoot_of_mem_nthRootsFinset hp hη hne1) + +end nthRootsFinsetPrime + section cyclic /-- If `G` is cyclic of order `n` and `G'` contains a primitive `n`th root of unity, diff --git a/Mathlib/RingTheory/SimpleRing/DivisionRing.lean b/Mathlib/RingTheory/SimpleRing/DivisionRing.lean new file mode 100644 index 00000000000000..b7e68ae02cae77 --- /dev/null +++ b/Mathlib/RingTheory/SimpleRing/DivisionRing.lean @@ -0,0 +1,59 @@ +/- +Copyright (c) 2026 Yunzhou Xie. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Edison Xie +-/ +module + +public import Mathlib.Algebra.Category.ModuleCat.Simple +public import Mathlib.RingTheory.SimpleModule.Basic + +/-! + +## Simple modules over division rings +This file contains some results about simple modules over division rings. + +# Main results + +* `DivisionRing.nonempty_linearEquiv_of_isSimpleModule` : There is an unique simple module over + a division ring, up to isomorphism. +* `isSimpleModule_iff_eq_zero_or_injective` : A module is simple if and only if it is nontrivial + and every linear map from it is either zero or injective, this is the module analogue of + `RingHom.injective` +* `IsSimpleModule.obj_of_isEquivalence` : If `M` is a simple module over a ring `R`, and + `e : ModuleCat R ⥤ ModuleCat S` is an equivalence of categories, + then `e(M)` is a simple module over `S`. + +## Tags +Noncommutative algebra, simple module, division ring + +-/ + +@[expose] public section + +universe u v + +open CategoryTheory + +variable (R S : Type*) [DivisionRing R] [DivisionRing S] (e : ModuleCat R ≌ ModuleCat S) + +lemma DivisionRing.nonempty_linearEquiv_of_isSimpleModule (N : Type*) [AddCommGroup N] + [Module S N] [IsSimpleModule S N] : Nonempty (N ≃ₗ[S] S) := by + obtain ⟨I, hI, ⟨e⟩⟩ := isSimpleModule_iff_quot_maximal.mp ‹_› + exact ⟨e ≪≫ₗ I.quotEquivOfEqBot ((eq_bot_or_eq_top I).resolve_right hI.ne_top)⟩ + +lemma isSimpleModule_iff_eq_zero_or_injective (R : Type u) (M : Type v) [Ring R] [AddCommGroup M] + [Module R M] : IsSimpleModule R M ↔ (Nontrivial M ∧ ∀ (N : Type v) [AddCommGroup N] + [Module R N] (f : M →ₗ[R] N), f = 0 ∨ Function.Injective f) := + ⟨fun hM ↦ ⟨Submodule.nontrivial_iff _|>.1 hM.1.1, fun N _ _ f ↦ hM.1.2 (LinearMap.ker f)|>.elim + (fun h ↦ Or.inr <| by rwa [LinearMap.ker_eq_bot] at h) (fun h ↦ Or.inl <|by simp_all)⟩, + fun ⟨hM1, hM2⟩ ↦ isSimpleModule_iff R M|>.2 ⟨fun p ↦ (hM2 (M ⧸ p) p.mkQ).elim + (fun h ↦ Or.inr <| by simpa [Submodule.ext_iff, LinearMap.ext_iff] using h) + (fun h ↦ Or.inl <| eq_bot_iff.2 fun x hx ↦ h (by simp [hx]))⟩⟩ + +lemma IsSimpleModule.obj_of_isEquivalence + {R S : Type*} [Ring R] [Ring S] (e : ModuleCat R ⥤ ModuleCat S) + [e.IsEquivalence] (M : ModuleCat R) [IsSimpleModule R M] : + IsSimpleModule S (e.obj M) := by + rw [← simple_iff_isSimpleModule'] at * + exact simple_obj e M diff --git a/Mathlib/RingTheory/Smooth/Basic.lean b/Mathlib/RingTheory/Smooth/Basic.lean index 1ba66377ae655e..54640dfe993ab0 100644 --- a/Mathlib/RingTheory/Smooth/Basic.lean +++ b/Mathlib/RingTheory/Smooth/Basic.lean @@ -177,7 +177,7 @@ theorem liftOfSurjective_apply [FormallySmooth R A] (f : A →ₐ[R] C) (g : B (hg : Function.Surjective g) (hg' : IsNilpotent <| RingHom.ker g) (x : A) : g (FormallySmooth.liftOfSurjective f g hg hg' x) = f x := by apply (Ideal.quotientKerAlgEquivOfSurjective hg).symm.injective - conv_rhs => rw [← AlgEquiv.coe_algHom, ← AlgHom.comp_apply, + conv_rhs => rw [← AlgEquiv.coe_toAlgHom, ← AlgHom.comp_apply, ← FormallySmooth.mk_lift (A := A) _ hg'] apply (Ideal.quotientKerAlgEquivOfSurjective hg).injective rw [AlgEquiv.apply_symm_apply, Ideal.quotientKerAlgEquivOfSurjective_apply] @@ -361,7 +361,7 @@ theorem of_comp_surjective refine ⟨g, AlgHom.ext fun x ↦ congr(f.kerSquareLift.kerLift ($hg x)).trans ?_⟩ obtain ⟨x, rfl⟩ := (Ideal.quotientKerAlgEquivOfSurjective surj).surjective x obtain ⟨x, rfl⟩ := Ideal.Quotient.mk_surjective x - simp only [AlgHom.toRingHom_eq_coe, AlgEquiv.coe_algHom, AlgEquiv.symm_apply_apply, + simp only [AlgHom.toRingHom_eq_coe, AlgEquiv.coe_toAlgHom, AlgEquiv.symm_apply_apply, AlgHom.coe_id, id_eq] simp only [Ideal.quotientKerAlgEquivOfSurjective_apply] diff --git a/Mathlib/RingTheory/Smooth/IntegralClosure.lean b/Mathlib/RingTheory/Smooth/IntegralClosure.lean index a412ca4415d48f..614a4369212324 100644 --- a/Mathlib/RingTheory/Smooth/IntegralClosure.lean +++ b/Mathlib/RingTheory/Smooth/IntegralClosure.lean @@ -66,7 +66,7 @@ lemma TensorProduct.toIntegralClosure_bijective_of_tower (AlgEquiv.ofBijective _ H').trans <| (AlgEquiv.mapIntegralClosure (Algebra.TensorProduct.cancelBaseChange ..)) convert! e.bijective - rw [← e.coe_algHom] + rw [← e.coe_toAlgHom] congr 1 ext; simp [e, toIntegralClosure] @@ -175,7 +175,7 @@ lemma TensorProduct.toIntegralClosure_bijective_of_isLocalization convert! (IsLocalization.algEquiv (Algebra.algebraMapSubmonoid (integralClosure R B) M) (S ⊗[R] integralClosure R B) (integralClosure S (S ⊗[R] B))).bijective - rw [← AlgHom.coe_restrictScalars' R, ← AlgEquiv.coe_restrictScalars' R, ← AlgEquiv.coe_algHom] + rw [← AlgHom.coe_restrictScalars' R, ← AlgEquiv.coe_restrictScalars' R, ← AlgEquiv.coe_toAlgHom] congr 1 ext1 · apply IsLocalization.algHom_ext M; ext @@ -354,7 +354,7 @@ theorem mem_adjoin_map_integralClosure_of_isStandardEtale [Algebra.IsStandardEta AlgEquiv.apply_symm_apply, map_pow, heg] simp_rw [mul_assoc, ← map_pow, show 𝓟.g.map (algebraMap R B) = 𝓟'.g from rfl, IsLocalization.mk'_spec'_mk, ← derivative_map]; rfl - · simp only [← AlgEquiv.coe_algHom, ← AlgHom.coe_toRingHom, ← RingHom.comp_apply, + · simp only [← AlgEquiv.coe_toAlgHom, ← AlgHom.coe_toRingHom, ← RingHom.comp_apply, ← coe_eval₂RingHom] congr 1 ext <;> simp [e, StandardEtalePair.equivAwayAdjoinRoot]; rfl diff --git a/Mathlib/RingTheory/Smooth/Pi.lean b/Mathlib/RingTheory/Smooth/Pi.lean index 117f16bd59492d..f4799e26092736 100644 --- a/Mathlib/RingTheory/Smooth/Pi.lean +++ b/Mathlib/RingTheory/Smooth/Pi.lean @@ -62,7 +62,7 @@ theorem pi_iff [Finite I] : fun _ ↦ Ideal.Quotient.mk_surjective _ replace he' : ∀ i, Ideal.Quotient.mk J (e i) = g (Pi.single i 1) := congr_fun he' let iso : B ≃ₐ[R] ∀ i, B ⧸ Ideal.span {1 - e i} := - { __ := Pi.algHom _ _ fun i ↦ Ideal.Quotient.mkₐ R _ + { __ := AlgHom.pi fun i ↦ Ideal.Quotient.mkₐ R _ __ := Equiv.ofBijective _ he.bijective_pi } let J' := fun i ↦ J.map (Ideal.Quotient.mk (Ideal.span {1 - e i})) let ι : ∀ i, (B ⧸ J →ₐ[R] (B ⧸ _) ⧸ J' i) := fun i ↦ Ideal.quotientMapₐ _ @@ -87,7 +87,7 @@ theorem pi_iff [Finite I] : (by rw [← Ideal.map_pow, hJ, Ideal.map_bot]) g' exact ⟨a, AlgHom.congr_fun ha⟩ choose a ha using this - use iso.symm.toAlgHom.comp (Pi.algHom _ _ fun i ↦ (a i).comp (Pi.evalAlgHom R A i)) + use iso.symm.toAlgHom.comp (AlgHom.pi fun i ↦ (a i).comp (Pi.evalAlgHom R A i)) ext x; rw [← AlgHom.toLinearMap_apply, ← AlgHom.toLinearMap_apply]; congr 1 ext i x simp only [AlgHom.comp_toLinearMap, AlgEquiv.toAlgHom_toLinearMap, diff --git a/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean b/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean index 9bd2c7935bdd97..70e090dc94f605 100644 --- a/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean +++ b/Mathlib/RingTheory/Spectrum/Maximal/Localization.lean @@ -174,7 +174,7 @@ theorem toPiLocalization_injective : Function.Injective (toPiLocalization R) := /-- The projection from the product of localizations at primes to the product of localizations at maximal ideals. -/ def piLocalizationToMaximal : PiLocalization R →ₐ[R] MaximalSpectrum.PiLocalization R := - Pi.algHom _ _ fun I ↦ Pi.evalAlgHom _ _ I.toPrimeSpectrum + AlgHom.pi fun I ↦ Pi.evalAlgHom _ _ I.toPrimeSpectrum open scoped Classical in theorem piLocalizationToMaximal_surjective : Function.Surjective (piLocalizationToMaximal R) := diff --git a/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean b/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean index 2ae9cab02f13e7..50ad1fe06dcaa0 100644 --- a/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean +++ b/Mathlib/RingTheory/Spectrum/Prime/FreeLocus.lean @@ -68,11 +68,7 @@ lemma mem_freeLocus_of_isLocalization (p : PrimeSpectrum R) intro r x obtain ⟨r, s, rfl⟩ := IsLocalization.exists_mk'_eq p.asIdeal.primeCompl r apply ((Module.End.isUnit_iff _).mp (IsLocalizedModule.map_units f s)).1 - simp only [e, AddHom.toFun_eq_coe, LinearMap.coe_toAddHom, LinearEquiv.coe_coe, - algebraMap_end_apply, - AlgEquiv.toRingEquiv_toRingHom, RingHom.coe_coe, IsLocalization.algEquiv_apply, - IsLocalization.map_id_mk'] - simp only [← map_smul, ← smul_assoc, IsLocalization.smul_mk'_self, algebraMap_smul] + simp [e, ← map_smul, ← smul_assoc] attribute [local instance] RingHomInvPair.of_ringEquiv in lemma mem_freeLocus_iff_tensor (p : PrimeSpectrum R) diff --git a/Mathlib/RingTheory/TensorProduct/Maps.lean b/Mathlib/RingTheory/TensorProduct/Maps.lean index d1b56d3faa6a5e..bc903ffb8e07dd 100644 --- a/Mathlib/RingTheory/TensorProduct/Maps.lean +++ b/Mathlib/RingTheory/TensorProduct/Maps.lean @@ -209,6 +209,19 @@ def liftEquiv : {fg : (A →ₐ[S] C) × (B →ₐ[R] C) // ∀ x y, Commute (fg left_inv fg := by ext <;> simp right_inv f' := by ext <;> simp +variable (R S B) in +/-- +Algebra maps `S ⊗[R] B →ₐ[S] C` are the same as algebra maps `B →ₐ[R] C`. +Variant of `Algebra.TensorProduct.liftEquiv` where the left map is fixed. +-/ +@[simps] +def liftEquivRight (C : Type*) [CommRing C] [Algebra R C] [Algebra S C] [IsScalarTower R S C] : + (B →ₐ[R] C) ≃ (S ⊗[R] B →ₐ[S] C) where + toFun f := Algebra.TensorProduct.lift (Algebra.ofId _ _) f fun _ _ ↦ .all _ _ + invFun f := AlgHom.comp (f.restrictScalars R) Algebra.TensorProduct.includeRight + left_inv _ := by ext; simp + right_inv _ := by ext; simp + theorem restrictScalars_lift [CommSemiring R'] [Algebra R R'] [Algebra R' S] [Algebra R' A] [IsScalarTower R R' A] [IsScalarTower R' S A] [Algebra R' C] [IsScalarTower R R' C] [IsScalarTower R' S C] @@ -399,7 +412,7 @@ omit [Algebra S A] [IsScalarTower R S A] attribute [local instance] Algebra.TensorProduct.rightAlgebra in /-- `S`-linear version of `Algebra.TensorProduct.comm` when `A ⊗[R] S` is viewed as an `S`-algebra via the right component. -/ -noncomputable def commRight : S ⊗[R] A ≃ₐ[S] A ⊗[R] S where +def commRight : S ⊗[R] A ≃ₐ[S] A ⊗[R] S where __ := Algebra.TensorProduct.comm R S A commutes' _ := rfl @@ -413,6 +426,7 @@ attribute [local instance] Algebra.TensorProduct.rightAlgebra in lemma commRight_symm_tmul (s : S) (a : A) : (commRight R S A).symm (a ⊗ₜ[R] s) = s ⊗ₜ a := rfl +set_option linter.dupNamespace false in @[deprecated (since := "2026-05-24")] alias Algebra.TensorProduct.commRight_symm_tmul := commRight_symm_tmul @@ -541,11 +555,11 @@ lemma comm_comp_map_apply (f : A →ₐ[R] C) (g : B →ₐ[R] D) (x) : variable (A) in /-- `lTensor A g : A ⊗ B →ₐ A ⊗ D` is the natural algebra morphism induced by `g : B →ₐ D`. -/ -noncomputable abbrev lTensor (g : B →ₐ[R] D) : (A ⊗[R] B) →ₐ[S] (A ⊗[R] D) := map (.id S A) g +abbrev lTensor (g : B →ₐ[R] D) : (A ⊗[R] B) →ₐ[S] (A ⊗[R] D) := map (.id S A) g variable (B) in /-- `rTensor B f : A ⊗ B →ₐ C ⊗ B` is the natural algebra morphism induced by `f : A →ₐ C`. -/ -noncomputable abbrev rTensor (f : A →ₐ[S] C) : A ⊗[R] B →ₐ[S] C ⊗[R] B := map f (.id R B) +abbrev rTensor (f : A →ₐ[S] C) : A ⊗[R] B →ₐ[S] C ⊗[R] B := map f (.id R B) /-- Construct an isomorphism between tensor products of an S-algebra with an R-algebra from S- and R- isomorphisms between the tensor factors. @@ -570,12 +584,12 @@ theorem congr_symm_apply (f : A ≃ₐ[S] C) (g : B ≃ₐ[R] D) (x) : @[simp] theorem congr_refl : congr (.refl : A ≃ₐ[S] A) (.refl : B ≃ₐ[R] B) = .refl := - AlgEquiv.coe_algHom_injective <| map_id + AlgEquiv.coe_toAlgHom_injective <| map_id theorem congr_trans (f₁ : A ≃ₐ[S] C) (f₂ : C ≃ₐ[S] E) (g₁ : B ≃ₐ[R] D) (g₂ : D ≃ₐ[R] F) : congr (f₁.trans f₂) (g₁.trans g₂) = (congr f₁ g₁).trans (congr f₂ g₂) := - AlgEquiv.coe_algHom_injective <| map_comp f₂.toAlgHom f₁.toAlgHom g₂.toAlgHom g₁.toAlgHom + AlgEquiv.coe_toAlgHom_injective <| map_comp f₂.toAlgHom f₁.toAlgHom g₂.toAlgHom g₁.toAlgHom theorem congr_symm (f : A ≃ₐ[S] C) (g : B ≃ₐ[R] D) : congr f.symm g.symm = (congr f g).symm := rfl @@ -733,7 +747,7 @@ def lmulEquiv [CompatibleSMul R S S S] : S ⊗[R] S ≃ₐ[S] S := theorem lmulEquiv_eq_lidOfCompatibleSMul [CompatibleSMul R S S S] : lmulEquiv R S = lidOfCompatibleSMul R S S := - AlgEquiv.coe_algHom_injective <| by ext; rfl + AlgEquiv.coe_toAlgHom_injective <| by ext; rfl /-- If `S` is commutative, for a pair of morphisms `f : A →ₐ[R] S`, `g : B →ₐ[R] S`, We obtain a map `A ⊗[R] B →ₐ[R] S` that commutes with `f`, `g` via `a ⊗ b ↦ f(a) * g(b)`. diff --git a/Mathlib/RingTheory/TensorProduct/Quotient.lean b/Mathlib/RingTheory/TensorProduct/Quotient.lean index f12cad92295d8e..14602fe0fed508 100644 --- a/Mathlib/RingTheory/TensorProduct/Quotient.lean +++ b/Mathlib/RingTheory/TensorProduct/Quotient.lean @@ -150,3 +150,44 @@ lemma Ideal.subtype_rTensor_range {R : Type*} [CommRing R] (M : Type*) [AddCommG ← Submodule.map_symm_eq_iff, ← Submodule.comap_equiv_eq_map_symm, ← LinearMap.ker_comp, ← TensorProduct.quotTensorEquivQuotSMul_comp_mkQ_rTensor, LinearEquiv.ker_comp] exact LinearMap.exact_iff.mp (rTensor_exact M (LinearMap.exact_subtype_mkQ I) I.mkQ_surjective) + +section + +variable {R R' R'' S : Type*} [CommRing R] [CommRing R'] [CommRing R''] [CommRing S] + [Algebra R R'] [Algebra R R''] [Algebra R' R''] [IsScalarTower R R' R''] [Algebra R S] + +variable (R'') in +set_option backward.isDefEq.respectTransparency false in +attribute [local ext high] Ideal.Quotient.algHom_ext in +/-- Let `e` be an element of `R' ⊗[R] S`. Then `R'' ⊗[R'] ((R' ⊗[R] S) / e)` is isomorphic to +`(R'' ⊗[R] S) / e` as `R''`-algebras. -/ +noncomputable +def Algebra.tensorQuotientTensorEquiv (e : R' ⊗[R] S) : + R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}) ≃ₐ[R''] + (R'' ⊗[R] S ⧸ Ideal.span {Algebra.TensorProduct.rTensor S (Algebra.ofId R' R'') e}) := + letI φ := Algebra.TensorProduct.rTensor S (Algebra.ofId R' R'') + letI ψ : R'' ⊗[R] S →ₐ[R''] R'' ⊗[R'] (R' ⊗[R] S ⧸ Ideal.span {e}) := + Algebra.TensorProduct.lift (Algebra.ofId _ _) + ((Algebra.TensorProduct.includeRight.restrictScalars R).comp + ((Ideal.Quotient.mkₐ _ _).comp Algebra.TensorProduct.includeRight)) fun _ _ ↦ .all _ _ + haveI hψφ : (ψ.restrictScalars R').comp φ = + (Algebra.TensorProduct.includeRight.restrictScalars R').comp (Ideal.Quotient.mkₐ _ _) := by + ext; simp [ψ, φ] + haveI heψ : Ideal.span {φ e} ≤ RingHom.ker ψ := by simpa [Ideal.span_le] using congr($hψφ e) + AlgEquiv.ofAlgHom (Algebra.TensorProduct.lift (Algebra.ofId _ _) (Ideal.quotientMapₐ _ φ + (Ideal.map_le_iff_le_comap.mp (by simp [Ideal.map_span, φ]))) fun _ _ ↦ .all _ _) + (Ideal.Quotient.liftₐ _ ψ heψ) (by ext; simp [ψ, φ]) (by ext; simp [φ, ψ]) + +@[simp] +lemma Algebra.tensorQuotientTensorEquiv_tmul (e : R' ⊗[R] S) (a : R'') (b : R') (c : S) : + Algebra.tensorQuotientTensorEquiv R'' e (a ⊗ₜ Ideal.Quotient.mk _ (b ⊗ₜ c)) = + Ideal.Quotient.mk _ ((a * algebraMap R' R'' b) ⊗ₜ c) := by + simp [Algebra.tensorQuotientTensorEquiv, ← Ideal.Quotient.mk_algebraMap, ← map_mul] + +@[simp] +lemma Algebra.tensorQuotientTensorEquiv_symm_tmul (e : R' ⊗[R] S) (a : R'') (b : S) : + (Algebra.tensorQuotientTensorEquiv R'' e).symm (Ideal.Quotient.mk _ (a ⊗ₜ b)) = + a ⊗ₜ Ideal.Quotient.mk _ (1 ⊗ₜ b) := by + simp [Algebra.tensorQuotientTensorEquiv] + +end diff --git a/Mathlib/RingTheory/TotallySplit.lean b/Mathlib/RingTheory/TotallySplit.lean index 5e6bb5483999f0..bc0467f1d9a2cc 100644 --- a/Mathlib/RingTheory/TotallySplit.lean +++ b/Mathlib/RingTheory/TotallySplit.lean @@ -99,6 +99,7 @@ lemma bijective_algebraMap_quotient [IsFiniteSplit k R] (p : Ideal R) [p.IsPrime variable (k R) in /-- If `R` is finite split over a field `k`, the `k`-rational points of `R` are in one-to-one correspondence with its prime spectrum. -/ +@[expose] noncomputable def algHomEquivPrimeSpectrum [IsFiniteSplit k R] : (R →ₐ[k] k) ≃ PrimeSpectrum R where toFun f := ⟨RingHom.ker f, RingHom.ker_isPrime f⟩ @@ -121,6 +122,11 @@ def algHomEquivPrimeSpectrum [IsFiniteSplit k R] : (R →ₐ[k] k) ≃ PrimeSpec AlgHom.ker_coe_equiv, ← RingHom.ker_eq_comap_bot, ← RingHom.ker_coe_toRingHom, Ideal.Quotient.mkₐ_ker] +@[simp] +lemma coe_algHomEquivPrimeSpectrum [IsFiniteSplit k R] (f : R →ₐ[k] k) : + algHomEquivPrimeSpectrum k R f = RingHom.ker f := + rfl + instance [IsSepClosed k] [EssFiniteType k R] [FormallyEtale k R] : IsFiniteSplit k R := by have := FormallyUnramified.finite_of_free k R have : IsArtinianRing R := isArtinian_of_tower k inferInstance diff --git a/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean b/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean index 543ded7a05f1ee..d9de6ad31362b3 100644 --- a/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean +++ b/Mathlib/RingTheory/TwoSidedIdeal/Basic.lean @@ -36,7 +36,9 @@ A two-sided ideal of a ring `R` is a subset of `R` that contains `0` and is clos negation, and absorbs multiplication on both sides. -/ structure TwoSidedIdeal (R : Type*) [NonUnitalNonAssocRing R] where - /-- every two-sided-ideal is induced by a congruence relation on the ring. -/ + /-- In a ring, every two-sided ideal is induced by a ring congruence relation. -/ + ofRingCon :: + /-- The congruence relation induced by this ideal. -/ ringCon : RingCon R end definitions @@ -69,9 +71,19 @@ instance : PartialOrder (TwoSidedIdeal R) := .ofSetLike (TwoSidedIdeal R) R lemma mem_iff (x : R) : x ∈ I ↔ I.ringCon x 0 := Iff.rfl @[simp] -lemma mem_mk {x : R} {c : RingCon R} : x ∈ mk c ↔ c x 0 := Iff.rfl +lemma mem_ofRingCon {x : R} {c : RingCon R} : x ∈ ofRingCon c ↔ c x 0 := Iff.rfl @[simp, norm_cast] +lemma coe_ofRingCon {c : RingCon R} : (ofRingCon c : Set R) = {x | c x 0} := rfl + +/-- A deprecated alias for `ofRingCon`. -/ +@[deprecated mk (since := "2026-06-18")] +abbrev mk (c : RingCon R) : TwoSidedIdeal R := ofRingCon c + +@[deprecated mem_ofRingCon (since := "2026-06-18")] +lemma mem_mk {x : R} {c : RingCon R} : x ∈ mk c ↔ c x 0 := Iff.rfl + +@[deprecated coe_ofRingCon (since := "2026-06-18")] lemma coe_mk {c : RingCon R} : (mk c : Set R) = {x | c x 0} := rfl lemma rel_iff (x y : R) : I.ringCon x y ↔ x - y ∈ I := by @@ -95,7 +107,7 @@ lemma le_iff {I J : TwoSidedIdeal R} : I ≤ J ↔ (I : Set R) ⊆ (J : Set R) : @[simps apply symm_apply] def orderIsoRingCon : TwoSidedIdeal R ≃o RingCon R where toFun := TwoSidedIdeal.ringCon - invFun := .mk + invFun := ofRingCon 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⟩ diff --git a/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean b/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean index 1fc23328611068..31958c93682821 100644 --- a/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean +++ b/Mathlib/RingTheory/TwoSidedIdeal/BigOperators.lean @@ -81,10 +81,8 @@ lemma finsetProd_mem {ι : Type*} (s : Finset ι) (f : ι → R) (hs : ∃ x ∈ rcases s simpa using multiSetProd_mem (hs := hs) -lemma finsuppProd_mem {ι : Type*} {β : Type*} {M : Type*} - [Add M] [CommMonoid M] [Zero β] - (h : ι → β → R) {f : ι →₀ β} (H : ∃ i ∈ f.support, h i (f i) ∈ I) : - f.prod h ∈ I := +lemma finsuppProd_mem {ι : Type*} {β : Type*} [Zero β] + (h : ι → β → R) {f : ι →₀ β} (H : ∃ i ∈ f.support, h i (f i) ∈ I) : f.prod h ∈ I := finsetProd_mem _ _ _ H lemma dfinsuppProd_mem {ι : Type*} {β : ι → Type*} diff --git a/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean b/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean index ad1b1e4d90c2ef..bc366c492530f2 100644 --- a/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean +++ b/Mathlib/RingTheory/TwoSidedIdeal/Kernel.lean @@ -31,7 +31,8 @@ variable (f : F) The kernel of a ring homomorphism, as a two-sided ideal. -/ def ker : TwoSidedIdeal R := - .mk + .ofRingCon + -- TODO: use `RingCon.ker` { r := fun x y ↦ f x = f y iseqv := by constructor <;> aesop mul' := by intro; simp_all diff --git a/Mathlib/RingTheory/UniqueFactorizationDomain/ClassGroup.lean b/Mathlib/RingTheory/UniqueFactorizationDomain/ClassGroup.lean index eab91759c51dd9..d71c4fdea1e966 100644 --- a/Mathlib/RingTheory/UniqueFactorizationDomain/ClassGroup.lean +++ b/Mathlib/RingTheory/UniqueFactorizationDomain/ClassGroup.lean @@ -9,6 +9,7 @@ public import Mathlib.RingTheory.ClassGroup.Basic /-! # The class group of a Unique Factorization Domain is trivial + This file proves that the ideal class group of a Normalized GCD Domain is trivial. The main application is to Unique Factorization Domains, which are known to be Normalized GCD Domains. diff --git a/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean b/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean index 3da613e7b67574..68c3a1a67d99de 100644 --- a/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean +++ b/Mathlib/RingTheory/UniqueFactorizationDomain/Defs.lean @@ -174,6 +174,14 @@ end UniqueFactorizationMonoid namespace UniqueFactorizationMonoid variable [CommMonoidWithZero α] + +variable (α) in +theorem of_subsingleton [Subsingleton α] : UniqueFactorizationMonoid α where + mul_left_cancel_of_ne_zero _ a b _ := Subsingleton.elim a b + mul_right_cancel_of_ne_zero _ a b _ := Subsingleton.elim a b + wf := ⟨fun a ↦ Acc.intro a fun b ⟨hb, _⟩ ↦ (hb (Subsingleton.elim b 0)).elim⟩ + irreducible_iff_prime {a} := by simp [Subsingleton.elim a 0] + variable [UniqueFactorizationMonoid α] open Classical in diff --git a/Mathlib/RingTheory/UniqueFactorizationDomain/Localization.lean b/Mathlib/RingTheory/UniqueFactorizationDomain/Localization.lean new file mode 100644 index 00000000000000..1c4b0fae487db3 --- /dev/null +++ b/Mathlib/RingTheory/UniqueFactorizationDomain/Localization.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Yongle Hu. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Yongle Hu +-/ +module + +public import Mathlib.RingTheory.Localization.Ideal +public import Mathlib.RingTheory.UniqueFactorizationDomain.Kaplansky + +/-! +# Localization of a UFD + +## Main results +* `UniqueFactorizationMonoid.localization` : The localization of a UFD is still a UFD. +-/ + +public section + +namespace UniqueFactorizationMonoid + +variable {R : Type*} [CommRing R] [UniqueFactorizationMonoid R] [IsDomain R] + +/-- If `S` is the localization of a UFD `R`, then `S` is also a UFD. -/ +theorem of_isLocalization (M : Submonoid R) + (S : Type*) [CommRing S] [Algebra R S] [IsLocalization M S] : UniqueFactorizationMonoid S := by + by_cases h0 : 0 ∈ M + · have : Subsingleton S := IsLocalization.subsingleton h0 + exact of_subsingleton S + have hM : M ≤ nonZeroDivisors R := le_nonZeroDivisors_of_noZeroDivisors h0 + have : IsDomain S := IsLocalization.isDomain_of_le_nonZeroDivisors S hM + rw [UniqueFactorizationMonoid.iff_exists_prime_mem_of_isPrime] + intro p hpb _ + obtain ⟨x, hxp, hpx⟩ := Ideal.IsPrime.exists_mem_prime_of_ne_bot + inferInstance (IsLocalization.bot_lt_under_prime M S hM p hpb).ne' + use algebraMap R S x, hxp + rw [← Ideal.span_singleton_prime] + · rw [← Set.image_singleton, ← Ideal.map_span] + refine IsLocalization.isPrime_of_isPrime_disjoint M S _ + (Ideal.isPrime_span_singleton_of_prime hpx) ?_ + rw [← IsLocalization.map_algebraMap_ne_top_iff_disjoint M S] + intro h + exact Ideal.IsPrime.ne_top' (top_unique (h.symm.trans_le (by simpa [Ideal.map_span] using hxp))) + · simp [map_ne_zero_iff _ (IsLocalization.injective S hM), hpx.ne_zero] + +/-- The localization of a UFD is still a UFD. -/ +instance localization (M : Submonoid R) : UniqueFactorizationMonoid (Localization M) := + of_isLocalization M (Localization M) + +end UniqueFactorizationMonoid diff --git a/Mathlib/RingTheory/Unramified/Locus.lean b/Mathlib/RingTheory/Unramified/Locus.lean index f227c41f69e57f..07c2c4d447fe01 100644 --- a/Mathlib/RingTheory/Unramified/Locus.lean +++ b/Mathlib/RingTheory/Unramified/Locus.lean @@ -89,6 +89,21 @@ theorem IsUnramifiedAt.residueField end +section IsUnramifiedIn + +variable {R : Type*} [CommRing R] + +/-- A prime `𝔭` of `R` is unramified in `A` if every prime ideal `𝔓` of `A` lying over `𝔭` is +unramified . -/ +def IsUnramifiedIn (A : Type*) [CommRing A] [Algebra R A] (𝔭 : Ideal R) : Prop := + ∀ (𝔓 : Ideal A) (_ : 𝔓.IsPrime), 𝔓.LiesOver 𝔭 → Algebra.IsUnramifiedAt R 𝔓 + +variable (A : Type*) [CommRing A] [Algebra R A] + +theorem isUnramifiedIn_top : IsUnramifiedIn A (⊤ : Ideal R) := + fun P hP _ ↦ (hP.ne_top ((Ideal.eq_top_iff_of_liesOver P (⊤ : Ideal R)).mpr rfl)).elim + +end IsUnramifiedIn section variable {R A : Type*} [CommRing R] [CommRing A] [Algebra R A] diff --git a/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean b/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean index 09454d77510d4b..0cb1d9d4957031 100644 --- a/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean +++ b/Mathlib/RingTheory/Valuation/ValuativeRel/Basic.lean @@ -568,8 +568,8 @@ instance : CommMonoidWithZero (ValueGroupWithZero R) where simp only [pow_succ, ← ValueGroupWithZero.mk_mul_mk, ih] apply congrArg (_ * ·) exact ValueGroupWithZero.sound h₁ h₂ - npow_zero := ValueGroupWithZero.ind (by simp) - npow_succ n := ValueGroupWithZero.ind (by simp [pow_succ]) + npow_zero := ValueGroupWithZero.ind (by simp_rw [HPow.hPow, Pow.pow]; simp) + npow_succ n := ValueGroupWithZero.ind (by simp_rw [HPow.hPow, Pow.pow]; simp [pow_succ]) instance : LE (ValueGroupWithZero R) where le := ValueGroupWithZero.lift₂ (fun a s b t => a * t ≤ᵥ b * s) <| by @@ -1333,7 +1333,7 @@ lemma leftInverse_embedding_orderMonoidIso : Function.LeftInverse embedding embedding_orderMonoidIso_valuation_eq /-- The isomorphism between `ValueGroupWithZero R` and `ValueGroup₀ (valuation R)`. -/ -@[deprecated "use ValueGroupWithZero.embed (valuation R) instead" (since := "2026-03-17")] +@[deprecated "use ValueGroupWithZero.orderMonoidIso instead" (since := "2026-03-17")] def valueGroupWithZero_equiv_valueGroup₀ := orderMonoidIso (valuation R) end ValueGroupWithZero diff --git a/Mathlib/RingTheory/ZMod/Torsion.lean b/Mathlib/RingTheory/ZMod/Torsion.lean index 88361d25c8bd28..04cac9f1a5b4f4 100644 --- a/Mathlib/RingTheory/ZMod/Torsion.lean +++ b/Mathlib/RingTheory/ZMod/Torsion.lean @@ -28,7 +28,6 @@ instance {p : ℕ} [Fact p.Prime] : HasEnoughRootsOfUnity (ZMod p) (p - 1) := by have : NeZero (p - 1) := ⟨by have : 2 ≤ p := Nat.Prime.two_le Fact.out; grind⟩ refine HasEnoughRootsOfUnity.of_card_le ?_ have := Nat.card_congr (MulEquiv.subgroupCongr (ZMod.rootsOfUnity_eq_top (p := p))).toEquiv - rw [Nat.card_eq_fintype_card] at this rw [this] simp [Fintype.card_units] diff --git a/Mathlib/RingTheory/ZariskisMainTheorem.lean b/Mathlib/RingTheory/ZariskisMainTheorem.lean index 56b7325c374066..920e9ed36be5b7 100644 --- a/Mathlib/RingTheory/ZariskisMainTheorem.lean +++ b/Mathlib/RingTheory/ZariskisMainTheorem.lean @@ -241,7 +241,7 @@ lemma exists_leadingCoeff_pow_smul_mem_conductor exists_isIntegral_leadingCoeff_pow_smul_sub_of_isIntegralElem_of_mul_mem_range φ _ p (hφ.to_isIntegral (t * x)) (by convert! this using 1; ring) obtain ⟨r, hr : algebraMap _ _ r = _⟩ := hRS.le hn - exact ⟨n, (C r + q), by simp [← Polynomial.algebraMap_eq, - Polynomial.algebraMap_apply, hr]⟩ + exact ⟨n, (C r + q), by simp [← Polynomial.algebraMap_eq, -Polynomial.algebraMap_apply, hr]⟩ choose n hn using this obtain ⟨s, hs⟩ := Module.Finite.fg_top (R := R[X]) (M := S) refine ⟨s.sup n, fun x ↦ ?_⟩ diff --git a/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean b/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean index f772f2c6ae1fb6..1bedf6696be382 100644 --- a/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean +++ b/Mathlib/SetTheory/Cardinal/Cofinality/Club.lean @@ -5,16 +5,16 @@ Authors: Violeta Hernández Palacios -/ module -public import Mathlib.Order.DirSupClosed -public import Mathlib.Order.IsNormal -public import Mathlib.SetTheory.Cardinal.Cofinality.Basic +public import Mathlib.SetTheory.Cardinal.Cofinality.Enum /-! -# Club sets +# Club sets and stationary sets -A subset of a well-ordered type `α` is called a club set when it is closed in the order topology and -cofinal. If `α` has no maximum, then an equivalent condition is that `α` is closed and unbounded; -hence the name. +A subset of a well-ordered type `α` is called a **club set** when it is closed in the order topology +and cofinal. If `α` has no maximum, then an equivalent condition is that `α` is closed and +unbounded; hence the name. + +A **stationary set** is a set which intersects all club sets. ## Implementation notes @@ -27,9 +27,12 @@ public section universe u v -open Cardinal Order +open Cardinal Order Set + +variable {α : Type v} {s t : Set α} {x : α} [LinearOrder α] -/-- A club set is closed under suprema and cofinal. -/ +/-- A club set is a set that is closed under suprema and that is cofinal. -/ +@[mk_iff] structure IsClub {α : Type*} [LinearOrder α] (s : Set α) where /-- Club sets are closed under suprema. If `α` is a well-order with the order topology, this condition is equivalent to `IsClosed s`. -/ @@ -40,8 +43,6 @@ structure IsClub {α : Type*} [LinearOrder α] (s : Set α) where namespace IsClub -variable {α : Type v} {s t : Set α} {x : α} [LinearOrder α] - @[simp] theorem of_isEmpty [IsEmpty α] {s : Set α} : IsClub s := ⟨.of_isEmpty, .of_isEmpty⟩ @@ -50,6 +51,12 @@ theorem of_isEmpty [IsEmpty α] {s : Set α} : IsClub s := protected theorem univ : IsClub (α := α) .univ := ⟨.univ, .univ⟩ +protected theorem nonempty [Nonempty α] (hs : IsClub s) : s.Nonempty := + hs.isCofinal.nonempty + +theorem _root_.isClub_empty_iff : IsClub (α := α) ∅ ↔ IsEmpty α := + ⟨fun h ↦ isCofinal_empty_iff.1 h.isCofinal, fun _ ↦ .of_isEmpty⟩ + protected theorem union (hs : IsClub s) (ht : IsClub t) : IsClub (s ∪ t) := ⟨hs.dirSupClosed.union ht.dirSupClosed, hs.isCofinal.mono Set.subset_union_left⟩ @@ -63,12 +70,12 @@ theorem csSup_mem {α} [ConditionallyCompleteLinearOrder α] {s t : Set α} theorem sInter_of_orderTop {s : Set (Set α)} [OrderTop α] (hs : ∀ x ∈ s, IsClub x) : IsClub (⋂₀ s) := by refine ⟨.sInter fun x hx ↦ (hs x hx).dirSupClosed, ?_⟩ - rw [isCofinal_iff_top_mem, Set.mem_sInter] + rw [isCofinal_iff_top_mem, mem_sInter] exact fun x hx ↦ (hs x hx).isCofinal.top_mem theorem iInter_of_orderTop {ι : Type*} {f : ι → Set α} [OrderTop α] (hs : ∀ i, IsClub (f i)) : IsClub (⋂ i, f i) := by - rw [← Set.sInter_range] + rw [← sInter_range] exact .sInter_of_orderTop (by simpa) theorem sInter_of_cof_le_one {s : Set (Set α)} (hα : cof α ≤ 1) (hs : ∀ x ∈ s, IsClub x) : @@ -80,11 +87,10 @@ theorem sInter_of_cof_le_one {s : Set (Set α)} (hα : cof α ≤ 1) (hs : ∀ x theorem iInter_of_cof_le_one {ι : Type*} {f : ι → Set α} (hα : cof α ≤ 1) (hs : ∀ i, IsClub (f i)) : IsClub (⋂ i, f i) := by - rw [← Set.sInter_range] + rw [← sInter_range] exact .sInter_of_cof_le_one hα (by simpa) section WellFoundedLT - variable [WellFoundedLT α] attribute [local instance] @@ -102,7 +108,7 @@ protected theorem sInter {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : #s refine .of_not_isCofinal fun hg ↦ (cof_le hg).not_gt (hα.trans_le' ?_) simpa using mk_range_le_lift (f := g) refine ⟨_, fun t ht ↦ ?_, le_csSup hg ⟨0, rfl⟩⟩ - apply (hs t ht).isLUB_mem (t := .range fun n ↦ f ⟨t, ht⟩ (g n)) _ (Set.range_nonempty _) + apply (hs t ht).isLUB_mem (t := .range fun n ↦ f ⟨t, ht⟩ (g n)) _ (range_nonempty _) · refine ⟨?_, fun b hb ↦ csSup_le' ?_⟩ <;> rintro _ ⟨n, rfl⟩ · apply (le_csSup (.of_not_isCofinal _) _).trans (le_csSup hg ⟨n + 1, rfl⟩) · exact fun hg' ↦ (cof_le hg').not_gt (mk_range_le.trans_lt hsα) @@ -113,19 +119,27 @@ protected theorem sInter {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : #s protected theorem iInter {ι : Type u} {f : ι → Set α} (hα : cof α ≠ ℵ₀) (hι : Cardinal.lift.{v} #ι < Cardinal.lift.{u} (cof α)) (hf : ∀ i, IsClub (f i)) : IsClub (⋂ i, f i) := by - rw [← Set.sInter_range] + rw [← sInter_range] refine IsClub.sInter hα ?_ (by simpa) rw [← Cardinal.lift_lt] exact mk_range_le_lift.trans_lt hι -protected theorem inter {s t : Set α} (hα : cof α ≠ ℵ₀) (hs : IsClub s) (ht : IsClub t) : - IsClub (s ∩ t) := by - rw [← Set.sInter_pair] - have H : ∀ x ∈ ({s, t} : Set _), IsClub x := by simpa [hs] - obtain hα | hα' := hα.lt_or_gt - · rw [cof_lt_aleph0_iff] at hα - exact .sInter_of_cof_le_one hα H - · exact .sInter hα (hα'.trans_le' <| by simp) H +theorem sInter_of_countable {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : s.Countable) + (hs : ∀ x ∈ s, IsClub x) : IsClub (⋂₀ s) := by + obtain hα | hα := hα.lt_or_gt + · apply IsClub.sInter_of_cof_le_one _ hs + rwa [← cof_lt_aleph0_iff] + · apply IsClub.sInter hα.ne' (hα.trans_le' _) hs + rwa [le_aleph0_iff_set_countable] + +theorem iInter_of_countable {ι : Sort*} {f : ι → Set α} [Countable ι] (hα : cof α ≠ ℵ₀) + (hf : ∀ i, IsClub (f i)) : IsClub (⋂ i, f i) := by + rw [← sInter_range] + apply IsClub.sInter_of_countable hα (countable_range f) + simpa + +protected theorem inter (hα : cof α ≠ ℵ₀) (hs : IsClub s) (ht : IsClub t) : IsClub (s ∩ t) := by + simpa [hs, ht] using IsClub.sInter_of_countable (s := {s, t}) hα theorem _root_.Order.IsNormal.isClub_range {f : α → α} (hf : IsNormal f) : IsClub (.range f) := ⟨hf.dirSupClosed_range, fun x ↦ ⟨_, ⟨x, rfl⟩, hf.strictMono.le_apply⟩⟩ @@ -134,7 +148,7 @@ theorem _root_.Order.IsNormal.isClub_fixedPoints {f : α → α} (hα : cof α IsClub f.fixedPoints := by cases isEmpty_or_nonempty α; · simp refine ⟨fun s hs hs₀ _ a ha ↦ (hf.map_isLUB ha hs₀).unique ?_, fun a ↦ ?_⟩ - · rwa [Set.image_congr hs, Set.image_id'] + · rwa [image_congr hs, image_id'] · cases topOrderOrNoTopOrder α with | inl => use ⊤; simpa using! hf.strictMono.id_le ⊤ | inr h => @@ -145,5 +159,126 @@ theorem _root_.Order.IsNormal.isClub_fixedPoints {f : α → α} (hα : cof α ((aleph0_le_cof.lt_of_ne' hα).trans_le' ?_) simpa using mk_range_le_lift (f := fun n : ℕ ↦ f^[n] a) +/-- Club sets in regular cardinals correspond one to one with normal functions. -/ +theorem _root_.Order.isNormal_enum_iff_isClub [IsRegularCardinalOrder α] + {s : Set α} {hs : IsCofinal s} : IsNormal (Subtype.val ∘ enum s hs) ↔ IsClub s := by + simp_rw [isClub_iff, hs, and_true, isNormal_enum_iff_dirSupClosed] + +theorem isNormal_enum [IsRegularCardinalOrder α] {s : Set α} (hs : IsClub s) : + IsNormal (Subtype.val ∘ enum s hs.isCofinal) := + isNormal_enum_iff_isClub.2 hs + end WellFoundedLT end IsClub + +/-! ### Stationary sets -/ + +/-- A set is called stationary when it intersects all club sets. -/ +@[expose] +def IsStationary (s : Set α) : Prop := + ∀ ⦃t⦄, IsClub t → (s ∩ t).Nonempty + +theorem not_isStationary_iff : ¬ IsStationary s ↔ ∃ t, IsClub t ∧ Disjoint s t := by + simp [IsStationary, disjoint_iff, not_nonempty_iff_eq_empty] + +@[gcongr] +theorem IsStationary.mono (hs : IsStationary s) (h : s ⊆ t) : IsStationary t := + fun _u hu ↦ (hs hu).mono (inter_subset_inter_left _ h) + +theorem IsStationary.nonempty (hs : IsStationary s) : s.Nonempty := by + simpa using hs .univ + +theorem isStationary_univ_iff : IsStationary (.univ (α := α)) ↔ Nonempty α := by + simp [IsStationary, ← not_imp_not (b := IsClub _), not_nonempty_iff_eq_empty, + isClub_empty_iff] + +@[simp] +protected theorem IsStationary.univ [Nonempty α] : IsStationary (.univ (α := α)) := + isStationary_univ_iff.2 ‹_› + +@[simp] +theorem not_isStationary_empty : ¬ IsStationary (∅ : Set α) := by + intro h + simpa using h .univ + +@[simp] +theorem not_isStationary_of_isEmpty [IsEmpty α] : ¬ IsStationary s := + s.eq_empty_of_isEmpty ▸ not_isStationary_empty + +theorem IsStationary.of_not_isCofinal_compl (hs : ¬ IsCofinal sᶜ) : IsStationary s := by + intro t ht + obtain ⟨a, ha⟩ := not_isCofinal_iff.1 hs + obtain ⟨b, hb, hb'⟩ := ht.isCofinal a + refine ⟨b, ?_, hb⟩ + contrapose! ha + exact ⟨b, ha, hb'⟩ + +theorem isStationary_sUnion_iff_of_cof_le_one {s : Set (Set α)} (hα : cof α ≤ 1) : + IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x where + mp h := by + contrapose! h + simp_rw [not_isStationary_iff] at h ⊢ + choose f hf hxf using h + refine ⟨⋂ x : s, f _ x.2, ?_, ?_⟩ + · apply IsClub.iInter_of_cof_le_one hα + simpa + · rw [disjoint_sUnion_left] + exact fun x hx ↦ (hxf _ hx).mono_right (iInter_subset _ ⟨x, hx⟩) + mpr := fun ⟨x, hxs, hx⟩ ↦ hx.mono (subset_sUnion_of_mem hxs) + +theorem isStationary_iUnion_iff_of_cof_le_one {ι : Sort*} {f : ι → Set α} (hα : cof α ≤ 1) : + IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) := by + rw [← sUnion_range, isStationary_sUnion_iff_of_cof_le_one hα] + simp + +theorem isStationary_sUnion_iff_of_orderTop [OrderTop α] {s : Set (Set α)} : + IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x := + isStationary_sUnion_iff_of_cof_le_one (by simp) + +theorem isStationary_iUnion_iff_of_orderTop [OrderTop α] {ι : Sort*} {f : ι → Set α} : + IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) := + isStationary_iUnion_iff_of_cof_le_one (by simp) + +section WellFoundedLT +variable [WellFoundedLT α] + +theorem IsClub.isStationary [Nonempty α] (hα : cof α ≠ ℵ₀) (hs : IsClub s) : IsStationary s := + fun _ ht ↦ (hs.inter hα ht).nonempty + +theorem isStationary_sUnion_iff {s : Set (Set α)} (hα : cof α ≠ ℵ₀) (hsα : #s < cof α) : + IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x where + mp h := by + contrapose! h + simp_rw [not_isStationary_iff] at h ⊢ + choose f hf hxf using h + refine ⟨⋂ x : s, f _ x.2, ?_, ?_⟩ + · apply IsClub.iInter hα <;> simpa + · rw [disjoint_sUnion_left] + exact fun x hx ↦ (hxf _ hx).mono_right (iInter_subset _ ⟨x, hx⟩) + mpr := fun ⟨x, hxs, hx⟩ ↦ hx.mono (subset_sUnion_of_mem hxs) + +theorem isStationary_iUnion_iff {ι : Type u} {f : ι → Set α} (hα : cof α ≠ ℵ₀) + (hι : lift.{v} #ι < lift.{u} (cof α)) : IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) := by + rw [← sUnion_range, isStationary_sUnion_iff hα] + · simp + · rw [← Cardinal.lift_lt] + exact mk_range_le_lift.trans_lt hι + +theorem isStationary_sUnion_iff_of_countable {s : Set (Set α)} (hα : cof α ≠ ℵ₀) + (hsα : s.Countable) : IsStationary (⋃₀ s) ↔ ∃ x ∈ s, IsStationary x := by + obtain hα | hα := hα.lt_or_gt + · apply isStationary_sUnion_iff_of_cof_le_one + rwa [← cof_lt_aleph0_iff] + · apply isStationary_sUnion_iff hα.ne' (hα.trans_le' _) + rwa [le_aleph0_iff_set_countable] + +theorem isStationary_iUnion_iff_of_countable {ι : Sort*} {f : ι → Set α} [Countable ι] + (hα : cof α ≠ ℵ₀) : IsStationary (⋃ i, f i) ↔ ∃ i, IsStationary (f i) := by + rw [← sUnion_range, isStationary_sUnion_iff_of_countable hα (countable_range f)] + simp + +theorem isStationary_union_iff (hα : cof α ≠ ℵ₀) : + IsStationary (s ∪ t) ↔ IsStationary s ∨ IsStationary t := by + simpa using isStationary_sUnion_iff_of_countable (s := {s, t}) hα + +end WellFoundedLT diff --git a/Mathlib/SetTheory/Cardinal/Cofinality/Enum.lean b/Mathlib/SetTheory/Cardinal/Cofinality/Enum.lean index 2c4170c98d118d..1fc97fcc7ed543 100644 --- a/Mathlib/SetTheory/Cardinal/Cofinality/Enum.lean +++ b/Mathlib/SetTheory/Cardinal/Cofinality/Enum.lean @@ -21,6 +21,11 @@ If `s` is a cofinal subset of a regular cardinal order `α`, there exists a uniq `α ≃o s`, which we call `Order.enum`. When `α = Ordinal`, this is referred to as the enumerator function of the set. Note that if `α = ℕ`, then this definition matches `Nat.nth`. +## Main results + +- `Order.enum_eq_iff`: `Order.enum s _` is the unique strictly monotonic function with range `s`. +- `Order.isNormal_enum_iff_dirSupClosed`: club sets correspond one to one with normal functions. + ## TODO - Deprecate `Ordinal.enumOrd` in favor of `Order.enum`. @@ -93,14 +98,16 @@ theorem type_eq_of_isCofinal {s : Set α} (hs : IsCofinal s) : typeLT s = typeLT noncomputable def enum (s : Set α) (hs : IsCofinal s) : α ≃o s := .ofRelIsoLT (type_eq.1 (type_eq_of_isCofinal hs).symm).some -theorem enum_le_of_forall_lt {a o : α} {s : Set α} {hs : IsCofinal s} (ho : o ∈ s) - (H : ∀ b < a, enum s hs b < o) : enum s hs a ≤ o := by +variable {s : Set α} {hs : IsCofinal s} + +theorem enum_le_of_forall_lt {a o : α} (ho : o ∈ s) (H : ∀ b < a, enum s hs b < o) : + enum s hs a ≤ o := by rw [← Subtype.coe_mk o ho, Subtype.coe_le_coe, ← OrderIso.le_symm_apply] apply le_of_forall_lt simpa [OrderIso.lt_symm_apply] -theorem enum_succ_le_of_lt [SuccOrder α] {a o : α} {s : Set α} {hs : IsCofinal s} (ha : o ∈ s) - (H : enum s hs a < o) : enum s hs (succ a) ≤ o := by +theorem enum_succ_le_of_lt [SuccOrder α] {a o : α} (ha : o ∈ s) (H : enum s hs a < o) : + enum s hs (succ a) ≤ o := by refine enum_le_of_forall_lt ha fun b hb ↦ H.trans_le' ?_ simpa using le_of_lt_succ hb @@ -109,4 +116,62 @@ theorem enum_univ (x : α) : enum univ .univ x = ⟨x, mem_univ x⟩ := by rw [← Subsingleton.allEq OrderIso.Set.univ.symm (enum univ .univ)] rfl +theorem enum_anti {hs : IsCofinal s} {t : Set α} {x : α} (h : s ⊆ t) : + enum t (hs.mono h) x ≤ (enum s hs x).1 := by + induction x using WellFoundedLT.induction with | ind x IH + exact enum_le_of_forall_lt (h (Subtype.prop _)) fun y hy ↦ + (IH y hy).trans_lt ((enum s hs).strictMono hy) + +/-- A characterization of `Order.enum s _`: it is the unique strictly monotone function +with range `s`. -/ +theorem enum_eq_iff {f : α → α} : Subtype.val ∘ enum s hs = f ↔ StrictMono f ∧ range f = s := by + have H := (Subtype.strictMono_coe _).comp (enum s hs).strictMono + constructor + · rintro rfl + use (Subtype.strictMono_coe _).comp (enum s hs).strictMono + simp + · rintro ⟨hf, rfl⟩ + rw [← StrictMono.range_inj H hf] + simp + rfl + +theorem enum_range {f : α → α} (hf : StrictMono f) : + enum (range f) (isCofinal_range_of_strictMono hf) = hf.orderIso := by + ext x + apply congrFun (enum_eq_iff.2 ⟨?_, ?_⟩) + · exact (Subtype.strictMono_coe _).comp (OrderIso.strictMono _) + · simp + +theorem enum_bot {α : Type*} [ConditionallyCompleteLinearOrderBot α] [WellFoundedLT α] + [IsRegularCardinalOrder α] {s : Set α} {hs : IsCofinal s} : enum s hs ⊥ = sInf s := by + let : Bot s := ⟨⟨sInf s, csInf_mem hs.nonempty⟩⟩ + let : OrderBot s := .mk fun a ↦ csInf_le' a.2 + rw [OrderIso.map_bot] + rfl + +/-- Club sets in regular cardinals correspond one to one with normal functions. + +See also `Order.isNormal_enum_iff_isClub`. -/ +theorem isNormal_enum_iff_dirSupClosed : + IsNormal (Subtype.val ∘ enum s hs) ↔ DirSupClosed s := by + let H := (Subtype.strictMono_coe _).comp (enum s hs).strictMono + refine ⟨fun he ↦ by simpa using he.dirSupClosed_range, ?_⟩ + rw [isNormal_iff, dirSupClosed_iff_of_linearOrder] + refine fun hs' ↦ ⟨H, fun a ha b hb ↦ ?_⟩ + have bdd : BddAbove (Subtype.val ∘ enum s hs '' Iio a) := by + use enum s hs a + simpa [upperBounds] using fun x hx ↦ hx.le + have : Nonempty α := ⟨a⟩ + let := WellFoundedLT.toOrderBot α + let := WellFoundedLT.conditionallyCompleteLinearOrderBot α + trans sSup ((Subtype.val ∘ enum s hs) '' Iio a) + · refine enum_le_of_forall_lt (hs' ?_ ?_ (isLUB_csSup' bdd)) fun b hb ↦ ?_ + · grind + · simpa using ha.ne_bot + · obtain ⟨c, hca, hbc⟩ := ha.lt_iff_exists_lt.1 hb + refine (H hbc).trans_le <| le_csSup bdd ⟨c, ?_⟩ + simpa + · apply csSup_le' + simpa [upperBounds] + end Order diff --git a/Mathlib/SetTheory/Cardinal/EventuallyConst.lean b/Mathlib/SetTheory/Cardinal/EventuallyConst.lean new file mode 100644 index 00000000000000..acdfa09f5982e9 --- /dev/null +++ b/Mathlib/SetTheory/Cardinal/EventuallyConst.lean @@ -0,0 +1,74 @@ +/- +Copyright (c) 2026 Violeta Hernández Palacios. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Violeta Hernández Palacios +-/ +module + +public import Mathlib.Order.Filter.EventuallyConst +public import Mathlib.SetTheory.Cardinal.Aleph + +/-! +# Eventually constant monotone functions + +This file proves variations of the following theorem: if `α` is a linear order and `β` is a partial +order with `#β < cof α`, then any monotone function `f : α → β` must be eventually constant. In +particular, this applies for functions from `Cardinal.{u}` or `Ordinal.{u}` into a `Small.{u}` type. +-/ + +public section + +universe u v + +variable {α : Type u} {β : Type v} [LinearOrder α] [PartialOrder β] + +open Cardinal Filter Order Set + +namespace Filter.EventuallyConst +variable {f : α → β} + +theorem of_not_isCofinal_rangeSplitting [Nonempty α] (hf : Monotone f) + (hf' : ¬ IsCofinal (range (rangeSplitting f))) : atTop.EventuallyConst f := by + rw [eventuallyConst_atTop] + obtain ⟨i, hi⟩ := not_isCofinal_iff.1 hf' + refine ⟨i, fun j hij ↦ (hf hij).antisymm' <| (hf (hi _ ⟨⟨f j, j, rfl⟩, rfl⟩).le).trans' ?_⟩ + rw [apply_rangeSplitting f] + +theorem of_monotone_of_lt_cof (hf : Monotone f) (hα : lift.{u} #β < lift.{v} (cof α)) : + atTop.EventuallyConst f := by + have : Nonempty α := by by_contra!; simp at hα + refine .of_not_isCofinal_rangeSplitting hf ?_ + contrapose! hα + classical let := hf.isChain_range.linearOrder + rw [← lift_cof_congr_of_strictMono (rangeSplitting_strictMono hf) hα, lift_le] + exact (cof_le_cardinalMk _).trans (mk_set_le _) + +theorem of_antitone_of_lt_cof (hf : Antitone f) (hα : lift.{u} #β < lift.{v} (cof α)) : + atTop.EventuallyConst f := + .of_monotone_of_lt_cof (β := βᵒᵈ) hf.dual_right hα + +end Filter.EventuallyConst + +namespace Cardinal +variable {f : Cardinal.{v} → β} [Small.{v} β] + +theorem eventuallyConst_of_monotone (hf : Monotone f) : atTop.EventuallyConst f := by + refine .of_monotone_of_lt_cof hf ?_ + simpa [← small_iff_lift_mk_lt_univ] + +theorem eventuallyConst_of_antitone (hf : Antitone f) : atTop.EventuallyConst f := + eventuallyConst_of_monotone (β := βᵒᵈ) hf + +end Cardinal + +namespace Ordinal +variable {f : Ordinal.{v} → β} [Small.{v} β] + +theorem eventuallyConst_of_monotone (hf : Monotone f) : atTop.EventuallyConst f := by + refine .of_monotone_of_lt_cof hf ?_ + simpa [← small_iff_lift_mk_lt_univ] + +theorem eventuallyConst_of_antitone (hf : Antitone f) : atTop.EventuallyConst f := + eventuallyConst_of_monotone (β := βᵒᵈ) hf + +end Ordinal diff --git a/Mathlib/SetTheory/Cardinal/Finite.lean b/Mathlib/SetTheory/Cardinal/Finite.lean index b473a4c4dc3384..e765a8b236bf85 100644 --- a/Mathlib/SetTheory/Cardinal/Finite.lean +++ b/Mathlib/SetTheory/Cardinal/Finite.lean @@ -84,6 +84,8 @@ lemma card_pos_iff : 0 < Nat.card α ↔ Nonempty α ∧ Finite α := by @[simp] lemma card_pos [Nonempty α] [Finite α] : 0 < Nat.card α := card_pos_iff.2 ⟨‹_›, ‹_›⟩ +instance [Nonempty α] [Finite α] : NeZero (Nat.card α) := ⟨card_pos.ne'⟩ + theorem finite_of_card_ne_zero (h : Nat.card α ≠ 0) : Finite α := (card_ne_zero.1 h).2 theorem card_congr (f : α ≃ β) : Nat.card α = Nat.card β := diff --git a/Mathlib/SetTheory/Cardinal/Order.lean b/Mathlib/SetTheory/Cardinal/Order.lean index eabd0ad6e5b4c1..600df08519e708 100644 --- a/Mathlib/SetTheory/Cardinal/Order.lean +++ b/Mathlib/SetTheory/Cardinal/Order.lean @@ -231,7 +231,7 @@ instance commSemiring : CommSemiring Cardinal.{u} where nsmul := nsmulRec npow n c := c ^ (n : Cardinal) npow_zero := power_zero - npow_succ n c := by rw [cast_succ, power_add, power_one] + npow_succ n c := by simp_rw [HPow.hPow, Pow.pow]; rw [cast_succ, power_add, power_one] natCast n := lift #(Fin n) natCast_zero := rfl natCast_succ n := cast_succ n diff --git a/Mathlib/SetTheory/Ordinal/Arithmetic.lean b/Mathlib/SetTheory/Ordinal/Arithmetic.lean index 30f55d840a5be4..6695747a689e33 100644 --- a/Mathlib/SetTheory/Ordinal/Arithmetic.lean +++ b/Mathlib/SetTheory/Ordinal/Arithmetic.lean @@ -768,8 +768,7 @@ theorem mul_add_div_mul {a c : Ordinal} (hc : c < a) (b d : Ordinal) : · have H := mul_ne_zero hc.ne_zero hd apply le_antisymm · rw [← lt_succ_iff, ← lt_mul_iff_div_lt H, mul_assoc] - · apply (add_lt_add_right hc _).trans_le - rw [← mul_succ] + · grw [hc, ← mul_succ] gcongr rw [succ_le_iff] exact lt_mul_succ_div b hd @@ -934,8 +933,7 @@ theorem lt_mul_iff {a b c : Ordinal} : a < b * c ↔ ∃ q < c, ∃ r < b, a = b obtain rfl | hb₀ := eq_or_ne b 0; · simp refine ⟨fun h ↦ ⟨_, (lt_mul_iff_div_lt hb₀).1 h, _, mod_lt a hb₀, (div_add_mod ..).symm⟩, ?_⟩ rintro ⟨q, hq, r, hr, rfl⟩ - apply add_lt_add_right hr _ |>.trans_le - grw [← mul_add_one, add_one_le_iff.2 hq] + grw [hr, ← mul_add_one, add_one_le_iff.2 hq] theorem forall_lt_mul {b c : Ordinal} {P : Ordinal → Prop} : (∀ a < b * c, P a) ↔ ∀ q < c, ∀ r < b, P (b * q + r) := by diff --git a/Mathlib/SetTheory/Ordinal/Basic.lean b/Mathlib/SetTheory/Ordinal/Basic.lean index d5641068269f63..689ae9ee2f849c 100644 --- a/Mathlib/SetTheory/Ordinal/Basic.lean +++ b/Mathlib/SetTheory/Ordinal/Basic.lean @@ -1269,6 +1269,10 @@ def ord.orderEmbedding : Cardinal ↪o Ordinal := theorem ord.orderEmbedding_coe : (ord.orderEmbedding : Cardinal → Ordinal) = ord := rfl +lemma nonempty_ord_toType {c : Cardinal} (h : c ≠ 0) : + Nonempty c.ord.ToType := by + rwa [Ordinal.nonempty_toType_iff, ne_eq, ord_eq_zero] + end Cardinal namespace Ordinal diff --git a/Mathlib/SetTheory/Ordinal/Exponential.lean b/Mathlib/SetTheory/Ordinal/Exponential.lean index 0a5fc651c4b1c4..69ffde62389d92 100644 --- a/Mathlib/SetTheory/Ordinal/Exponential.lean +++ b/Mathlib/SetTheory/Ordinal/Exponential.lean @@ -432,8 +432,7 @@ theorem log_opow_mul_add {b u v w : Ordinal} (hb : 1 < b) (hv : v ≠ 0) (hw : w rw [log_eq_iff hb] · constructor · grw [opow_add, opow_log_le_self b hv, ← le_self_add] - · apply (add_lt_add_right hw _).trans_le - rw [← mul_add_one, add_assoc, opow_add] + · grw [hw, ← mul_add_one, add_assoc, opow_add] gcongr rw [add_one_le_iff] exact lt_opow_succ_log_self hb _ diff --git a/Mathlib/SetTheory/Ordinal/Notation.lean b/Mathlib/SetTheory/Ordinal/Notation.lean index 276eb23a77ca1a..5adbc6f5b49997 100644 --- a/Mathlib/SetTheory/Ordinal/Notation.lean +++ b/Mathlib/SetTheory/Ordinal/Notation.lean @@ -271,7 +271,7 @@ theorem oadd_lt_oadd_1 {e₁ n₁ o₁ e₂ n₂ o₂} (h₁ : NF (oadd e₁ n theorem oadd_lt_oadd_2 {e o₁ o₂ : ONote} {n₁ n₂ : ℕ+} (h₁ : NF (oadd e n₁ o₁)) (h : (n₁ : ℕ) < n₂) : oadd e n₁ o₁ < oadd e n₂ o₂ := by simp only [lt_def, repr] - refine (add_lt_add_right h₁.snd'.repr_lt _).trans_le (le_trans ?_ le_self_add) + grw [h₁.snd'.repr_lt, ← le_self_add] rwa [← mul_succ, mul_le_mul_iff_right₀ (opow_pos _ omega0_pos), succ_le_iff, Nat.cast_lt] theorem oadd_lt_oadd_3 {e n a₁ a₂} (h : a₁ < a₂) : oadd e n a₁ < oadd e n a₂ := by @@ -529,14 +529,13 @@ theorem oadd_mul_nfBelow {e₁ n₁ a₁ b₁} (h₁ : NFBelow (oadd e₁ n₁ a have IH := oadd_mul_nfBelow h₁ h₂.snd by_cases e0 : e₂ = 0 <;> simp only [e0, oadd_mul, ↓reduceIte] · apply NFBelow.oadd h₁.fst h₁.snd - simpa using (add_lt_add_iff_left (repr e₁)).2 h₂.lt.pos + grw [← h₂.lt.pos, add_zero] · haveI := h₁.fst haveI := h₂.fst apply NFBelow.oadd · infer_instance · rwa [repr_add] - · rw [repr_add, add_lt_add_iff_left] - exact h₂.lt + · grw [repr_add, h₂.lt] instance mul_nf : ∀ (o₁ o₂) [NF o₁] [NF o₂], NF (o₁ * o₂) | 0, o, _, h₂ => by cases o <;> exact NF.zero diff --git a/Mathlib/SetTheory/Ordinal/Topology.lean b/Mathlib/SetTheory/Ordinal/Topology.lean index 298c9b4ea797c0..263dd7e58345c2 100644 --- a/Mathlib/SetTheory/Ordinal/Topology.lean +++ b/Mathlib/SetTheory/Ordinal/Topology.lean @@ -5,6 +5,7 @@ Authors: Violeta Hernández Palacios -/ module +public import Mathlib.SetTheory.Cardinal.Cofinality.Enum public import Mathlib.SetTheory.Ordinal.Enum public import Mathlib.Tactic.TFAE public import Mathlib.Topology.Order.IsNormal @@ -141,6 +142,7 @@ theorem isClosed_iff_bsup : theorem isSuccLimit_of_mem_frontier (ha : a ∈ frontier s) : IsSuccLimit a := SuccOrder.isSuccLimit_of_mem_frontier ha +@[deprecated isNormal_enum_iff_dirSupClosed (since := "2026-05-25")] theorem enumOrd_isNormal_iff_isClosed (hs : ¬ BddAbove s) : IsNormal (enumOrd s) ↔ IsClosed s := by have Hs := enumOrd_strictMono hs diff --git a/Mathlib/SetTheory/Ordinal/Univ.lean b/Mathlib/SetTheory/Ordinal/Univ.lean index d7aac64cfe94d2..5b302b22694094 100644 --- a/Mathlib/SetTheory/Ordinal/Univ.lean +++ b/Mathlib/SetTheory/Ordinal/Univ.lean @@ -28,19 +28,21 @@ This makes the basic API easier to set up. See `Cardinal.mk_cardinal` for a proo universe u v w +set_option linter.checkUnivs false in open Ordinal in -- intended to be used with explicit universe parameters /-- The ordinal `univ.{u, v}` is the order type of `Ordinal.{u}` or `Cardinal.{u}`, as an element of `Ordinal.{v}` (when `u < v`). -/ -@[pp_with_univ, nolint checkUnivs] +@[pp_with_univ] def Ordinal.univ : Ordinal.{max (u + 1) v} := lift.{v, u + 1} (typeLT Ordinal) +set_option linter.checkUnivs false in open Cardinal in -- intended to be used with explicit universe parameters /-- The cardinal `univ.{u, v}` is the cardinality of `Ordinal.{u}` or `Cardinal.{u}`, as an element of `Cardinal.{v}` (when `u < v`). -/ -@[pp_with_univ, nolint checkUnivs] +@[pp_with_univ] def Cardinal.univ : Cardinal.{max (u + 1) v} := lift.{v, u + 1} #Ordinal diff --git a/Mathlib/SetTheory/ZFC/PSet.lean b/Mathlib/SetTheory/ZFC/PSet.lean index 70115837262919..1e6516a0bbdc19 100644 --- a/Mathlib/SetTheory/ZFC/PSet.lean +++ b/Mathlib/SetTheory/ZFC/PSet.lean @@ -441,8 +441,8 @@ protected def Lift : PSet.{u} → PSet.{max u v} | ⟨α, A⟩ => ⟨ULift.{v, u} α, fun ⟨x⟩ => PSet.Lift (A x)⟩ -- intended to be used with explicit universe parameters +set_option linter.checkUnivs false in /-- Embedding of one universe in another -/ -@[nolint checkUnivs] def embed : PSet.{max (u + 1) v} := ⟨ULift.{v, u + 1} PSet, fun ⟨x⟩ => PSet.Lift.{u, max (u + 1) v} x⟩ diff --git a/Mathlib/Tactic.lean b/Mathlib/Tactic.lean index 50686dca468d2e..40152d484fd5dd 100644 --- a/Mathlib/Tactic.lean +++ b/Mathlib/Tactic.lean @@ -2,6 +2,7 @@ module -- shake: keep-all --deprecated_module: ignore public import Mathlib.Tactic.Abel public import Mathlib.Tactic.AdaptationNote +public import Mathlib.Tactic.Algebra.AlgebraNF public import Mathlib.Tactic.Algebra.Basic public import Mathlib.Tactic.Algebra.Lemmas public import Mathlib.Tactic.Algebraize @@ -98,6 +99,9 @@ public import Mathlib.Tactic.DeriveCountable public import Mathlib.Tactic.DeriveEncodable public import Mathlib.Tactic.DeriveFintype public import Mathlib.Tactic.DeriveTraversable +public import Mathlib.Tactic.Determinant.Bird +public import Mathlib.Tactic.Determinant.Bird.Cert +public import Mathlib.Tactic.Determinant.Bird.Meta public import Mathlib.Tactic.ENatToNat public import Mathlib.Tactic.Eqns public import Mathlib.Tactic.ErwQuestion @@ -140,6 +144,7 @@ public import Mathlib.Tactic.GRewrite public import Mathlib.Tactic.GRewrite.Core public import Mathlib.Tactic.GRewrite.Elab public import Mathlib.Tactic.Generalize +public import Mathlib.Tactic.GrindAttrs public import Mathlib.Tactic.Group public import Mathlib.Tactic.GuardGoalNums public import Mathlib.Tactic.GuardHypNums @@ -257,6 +262,8 @@ public import Mathlib.Tactic.Order.ToInt public import Mathlib.Tactic.PNatToNat public import Mathlib.Tactic.PPWithUniv public import Mathlib.Tactic.Peel +public import Mathlib.Tactic.Polynomial.Basic +public import Mathlib.Tactic.Polynomial.Core public import Mathlib.Tactic.Polyrith public import Mathlib.Tactic.Positivity public import Mathlib.Tactic.Positivity.Basic diff --git a/Mathlib/Tactic/Abel.lean b/Mathlib/Tactic/Abel.lean index 757090587b50a7..32543310433274 100644 --- a/Mathlib/Tactic/Abel.lean +++ b/Mathlib/Tactic/Abel.lean @@ -377,11 +377,11 @@ partial def eval (e : Expr) : M (NormalExpr × Expr) := do let (e₁, p₁) ← eval e let (e₂, p₂) ← evalNeg e₁ return (e₂, ← iapp `Mathlib.Tactic.Abel.subst_into_neg #[e, e₁, e₂, p₁, p₂]) - | (``AddMonoid.nsmul, #[_, _, e₁, e₂]) => do + | (``NSMul.nsmul, #[_, _, e₁, e₂]) => do let n ← if (← read).isGroup then mkAppM ``Int.ofNat #[e₁] else pure e₁ let (e', p) ← eval <| ← iapp ``smul #[n, e₂] return (e', ← iapp ``unfold_smul #[e₁, e₂, e', p]) - | (``SubNegMonoid.zsmul, #[_, _, e₁, e₂]) => do + | (``ZSMul.zsmul, #[_, _, e₁, e₂]) => do if ¬ (← read).isGroup then failure let (e', p) ← eval <| ← iapp ``smul #[e₁, e₂] return (e', (← read).app ``unfold_zsmul (← read).inst #[e₁, e₂, e', p]) @@ -411,8 +411,8 @@ def isAtom (e : Expr) : Bool := | (``HAdd.hAdd, #[_, _, _, _, _, _]) | (``HSub.hSub, #[_, _, _, _, _, _]) | (``Neg.neg, #[_, _, _]) - | (``AddMonoid.nsmul, #[_, _, _, _]) - | (``SubNegMonoid.zsmul, #[_, _, _, _]) + | (``NSMul.nsmul, #[_, _, _, _]) + | (``ZSMul.zsmul, #[_, _, _, _]) | (``SMul.smul, #[.const ``Int _, _, _, _, _]) | (``SMul.smul, #[.const ``Nat _, _, _, _, _]) | (``HSMul.hSMul, #[.const ``Int _, _, _, _, _, _]) diff --git a/Mathlib/Tactic/Algebra/AlgebraNF.lean b/Mathlib/Tactic/Algebra/AlgebraNF.lean new file mode 100644 index 00000000000000..04730c671c32ba --- /dev/null +++ b/Mathlib/Tactic/Algebra/AlgebraNF.lean @@ -0,0 +1,77 @@ +/- +Copyright (c) 2025 Arend Mellendijk. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Arend Mellendijk +-/ +module + +public import Mathlib.Tactic.Algebra.Basic + +/-! # The `algebra_nf` tactic + +This file contains helper functions for the (currently unimplemented) `algebra_nf` tactic. + +The defnitions in this file are currently only used by `polynomial_nf`. +-/ + +public meta section + +open Lean Meta Qq Mathlib.Tactic.Ring + +namespace Mathlib.Tactic.Algebra + +/-- Clean up the normal form into a more human-friendly format. This does everything + `RingNF.cleanup` does and also pulls the scalar multiplication from the end of of each term to + the start. i.e. x * y * (r • 1) → r • (x * y) + Used by `cleanup`. -/ +def cleanupSMul (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do + let thms : SimpTheorems := {} + let thms ← [``add_zero, ``add_assoc_rev, ``_root_.mul_one, ``mul_assoc_rev, ``_root_.pow_one, + ``mul_neg, ``add_neg, ``one_smul, ``mul_smul_comm, ``Algebra.algebraMap_eq_smul_one + ].foldlM (·.addConst ·) thms + let thms ← [``nat_rawCast_0, ``nat_rawCast_1, ``nat_rawCast_2, ``int_rawCast_neg, + ``nnrat_rawCast, ``rat_rawCast_neg].foldlM (·.addConst · (post := false)) thms + let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta } + (simpTheorems := #[thms]) + (congrTheorems := ← getSimpCongrTheorems) + pure <| ← + r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1 + +/-- Turn scalar multiplication by an explicit constant in `R` into multiplication in `A`. + +e.g. `(4 : ℚ) • x` becomes `4 * x` but `↑n • x` stays `↑n • x`. +-/ +def cleanupConsts (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do + let thms : SimpTheorems := {} + let thms ← [``add_zero, ``_root_.one_mul, ``_root_.mul_one, + ``neg_mul, ``add_neg].foldlM (·.addConst ·) thms + let thms ← [``ofNat_smul, ``neg_ofNat_smul, ``neg_1_smul, ``nnRat_ofNat_smul_1, + ``nnRat_ofNat_smul_2, ``rat_ofNat_smul_1, ``rat_ofNat_smul_2 + ].foldlM (·.addConst · (post := false)) thms + let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta } + (simpTheorems := #[thms]) + (congrTheorems := ← getSimpCongrTheorems) + pure <| ← + r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1 + +/-- The core of `algebra_nf with R` - normalize the expression `e` over the base ring `R` +Also used internally in `polynomial_nf`. -/ +meta def evalExpr {u : Lean.Level} (R : Q(Type u)) (e : Expr) : AtomM Simp.Result := do + let e ← withReducible <| whnf e + guard e.isApp -- all interesting ring expressions are applications + let ⟨v, A, e⟩ ← inferTypeQ' e + let sA ← synthInstanceQ q(CommSemiring $A) + let sR ← synthInstanceQ q(CommSemiring $R) + let sAlg ← synthInstanceQ q(Algebra $R $A) + let cr ← Algebra.mkCache sR + let ca ← Algebra.mkCache sA + assumeInstancesCommute + let ⟨a, _, pa⟩ ← match + ← Common.isAtomOrDerivable (Algebra.ringCompute q($sAlg) cr ca) ca.toCache q($e) with + -- `none` indicates that `eval` will find something algebraic. + | none => Common.eval rcℕ (Algebra.ringCompute sAlg cr ca) ca.toCache e + | some none => failure -- No point rewriting atoms + | some (some r) => pure r -- Nothing algebraic for `eval` to use, but `norm_num` simplifies. + pure { expr := a, proof? := pa } + +end Mathlib.Tactic.Algebra diff --git a/Mathlib/Tactic/Algebra/Basic.lean b/Mathlib/Tactic/Algebra/Basic.lean index d71ca00b7cd5db..196a24afa6a3cd 100644 --- a/Mathlib/Tactic/Algebra/Basic.lean +++ b/Mathlib/Tactic/Algebra/Basic.lean @@ -9,9 +9,9 @@ public meta import Lean.Meta.Tactic.NormCast public import Mathlib.Tactic.Algebra.Lemmas public import Mathlib.Tactic.Ring.RingNF - /-! # The `algebra` tactic + A suite of three tactics for solving equations in commutative algebras over commutative (semi)rings, where the exponents can also contain variables. @@ -164,24 +164,24 @@ namespace RingCompute /-- Evaluate the sum of two normalized expressions in `R` using `ring`. -/ def add (cR : Common.Cache sR) {a b : Q($A)} (za : BaseType sAlg a) (zb : BaseType sAlg b) : - MetaM (Common.Result (BaseType sAlg) q($a + $b) × Option Q(IsNat ($a + $b) 0)) := do - let ⟨r, vr⟩ := za - let ⟨s, vs⟩ := zb - let ⟨t, vt, pt⟩ ← Common.evalAdd (Ring.ringCompute cR) rcℕ vr vs - match vt with - | .zero => - have : $t =Q 0 := ⟨⟩ - return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, some q(add_algebraMap_isNat_zero $pt)⟩ - | vt => - return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, none⟩ + MetaM (Common.Result (BaseType sAlg) q($a + $b) × Option Q(IsNat ($a + $b) 0)) := + match za, zb with + | .mk r vr, .mk s vs => do + let ⟨t, vt, pt⟩ ← Common.evalAdd (Ring.ringCompute cR) rcℕ vr vs + match (dependent := true) vt with + | .zero => + have : $t =Q 0 := ⟨⟩ + return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, some q(add_algebraMap_isNat_zero $pt)⟩ + | vt => + return ⟨⟨_, .mk _ vt, q(add_algebraMap $pt)⟩, none⟩ /-- Evaluate the product of two normalized expressions in `R` using `ring`. -/ def mul (cR : Common.Cache sR) {a b : Q($A)} (za : BaseType sAlg a) (zb : BaseType sAlg b) : - MetaM (Common.Result (BaseType sAlg) q($a * $b)) := do - let ⟨r, vr⟩ := za - let ⟨s, vs⟩ := zb - let ⟨t, vt, pt⟩ ← Common.evalMul (Ring.ringCompute cR) rcℕ vr vs - return ⟨_, .mk _ vt, q(by simp [← $pt, map_mul])⟩ + MetaM (Common.Result (BaseType sAlg) q($a * $b)) := + match za, zb with + | .mk r vr, .mk s vs => do + let ⟨t, vt, pt⟩ ← Common.evalMul (Ring.ringCompute cR) rcℕ vr vs + return ⟨_, .mk _ vt, q(by simp [← $pt, map_mul])⟩ /-- Take an expression `r'` in a ring `R'` such that `R` is an `R'`-algebra and cast `r'` to `R` using `algebraMap R' R`, so that the scalar multiplication action on `A` is preserved. -/ @@ -194,7 +194,7 @@ def cast (cR : Algebra.Cache sR) (u' : Level) (R' : Q(Type u')) let ⟨r, pf_smul⟩ ← evalSMulCast q($sAlg) q($_smul) r' let ⟨_r'', vr, pr⟩ ← Common.eval rcℕ (Ring.ringCompute cR.toCache) cR.toCache q($r) - match vr with + match (dependent := true) vr with | .zero .. => assumeInstancesCommute return ⟨_, .zero, q(cast_zero_smul_eq_zero_mul $pr $pf_smul)⟩ @@ -205,37 +205,40 @@ def cast (cR : Algebra.Cache sR) (u' : Level) (R' : Q(Type u')) /-- Evaluate the product of two normalized expressions in `R` using `ring`. -/ def neg (cR : Algebra.Cache sR) {a : Q($A)} (_rA : Q(CommRing $A)) (za : BaseType sAlg a) : - MetaM (Common.Result (BaseType sAlg) q(-$a)) := do - let ⟨r, vr⟩ := za - match cR.rα with - | some rR => - let ⟨_, vt, pt⟩ ← Common.evalNeg (Ring.ringCompute cR.toCache) q($rR) vr - assumeInstancesCommute - return ⟨_, .mk _ vt, q(neg_algebraMap $pt)⟩ - | none => failure + MetaM (Common.Result (BaseType sAlg) q(-$a)) := + match za with + | .mk r vr => do + match cR.rα with + | some rR => + let ⟨_, vt, pt⟩ ← Common.evalNeg (Ring.ringCompute cR.toCache) q($rR) vr + assumeInstancesCommute + return ⟨_, .mk _ vt, q(neg_algebraMap $pt)⟩ + | none => failure /-- Raise a normalized expression in `R` to the power of a normalized natural number expression using `ring`. -/ def pow (cR : Common.Cache sR) {a : Q($A)} {b : Q(ℕ)} (za : BaseType sAlg a) (vb : Common.ExProdNat q($b)) : - OptionT MetaM (Common.Result (BaseType sAlg) q($a ^ $b)) := do - let ⟨r, vr⟩ := za - let ⟨_, vs, ps⟩ ← Common.evalPow₁ (Ring.ringCompute cR) rcℕ vr vb - return ⟨_, ⟨_, vs⟩, q(pow_algebraMap $ps)⟩ + OptionT MetaM (Common.Result (BaseType sAlg) q($a ^ $b)) := + match za with + | .mk r vr => do + let ⟨_, vs, ps⟩ ← Common.evalPow₁ (Ring.ringCompute cR) rcℕ vr vb + return ⟨_, ⟨_, vs⟩, q(pow_algebraMap $ps)⟩ /-- Evaluate the inverse of two normalized expressions in `R` using `ring`. -/ /- We include the CharZero argument to match the type signature of the ringCompute entry. -/ @[nolint unusedArguments] def inv (cR : Algebra.Cache sR) {a : Q($A)} (_ : Option Q(CharZero $A)) (fA : Q(Semifield $A)) - (za : BaseType sAlg a) : AtomM (Option (Common.Result (BaseType sAlg) q($a⁻¹))) := do - match cR.dsα with - | some fR => - let ⟨r, vr⟩ := za - let ⟨_, vs, ps⟩ ← Common.ExSum.evalInv (Ring.ringCompute cR.toCache) rcℕ q($fR) cR.czα vr - assumeInstancesCommute - return some ⟨_, ⟨_, vs⟩, q(inv_algebraMap $ps)⟩ - | none => - return none + (za : BaseType sAlg a) : AtomM (Option (Common.Result (BaseType sAlg) q($a⁻¹))) := + match za with + | .mk r vr => do + match cR.dsα with + | some fR => + let ⟨_, vs, ps⟩ ← Common.ExSum.evalInv (Ring.ringCompute cR.toCache) rcℕ q($fR) cR.czα vr + assumeInstancesCommute + return some ⟨_, ⟨_, vs⟩, q(inv_algebraMap $ps)⟩ + | none => + return none /-- Evaluate constants in `A` using `norm_num`. -/ def derive (cR : Algebra.Cache sR) (cA : Algebra.Cache sA) (x : Q($A)) : @@ -292,59 +295,17 @@ open Lean Parser.Tactic Elab Command Elab.Tactic Meta Qq theorem Nat.cast_eq_algebraMap (A : Type*) [CommSemiring A] (n : ℕ) : Nat.cast n = algebraMap ℕ A n := rfl -theorem Nat.algebraMap_eq_cast (A : Type*) [CommSemiring A] (n : ℕ) : - algebraMap ℕ A n = Nat.cast n := rfl - theorem Int.cast_eq_algebraMap (A : Type*) [CommRing A] (n : ℤ) : Int.cast n = algebraMap ℤ A n := rfl -theorem Int.algebraMap_eq_cast (A : Type*) [CommRing A] (n : ℤ) : - algebraMap ℤ A n = Int.cast n := rfl - /-- Remove some nonstandard spellings of `algebraMap` such as `Nat.cast` -/ -def preprocess (mvarId : MVarId) : MetaM MVarId := do +def preprocess (e : Expr) : MetaM Simp.Result := do -- collect the available `push_cast` lemmas let thms : SimpTheorems := {} let thms ← [``Nat.cast_eq_algebraMap, ``Int.cast_eq_algebraMap, ``Algebra.algebraMap_eq_smul_one].foldlM (·.addConst ·) thms let ctx ← Simp.mkContext { failIfUnchanged := false } (simpTheorems := #[thms]) - let (some r, _) ← simpTarget mvarId ctx (simprocs := #[]) | - throwError "internal error in polynomial tactic: preprocessing should not close goals" - return r - -/-- Clean up the normal form into a more human-friendly format. This does everything - `RingNF.cleanup` does and also pulls the scalar multiplication from the end of of each term to - the start. i.e. x * y * (r • 1) → r • (x * y) - Used by `cleanup`. -/ -def cleanupSMul (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do - let thms : SimpTheorems := {} - let thms ← [``add_zero, ``add_assoc_rev, ``_root_.mul_one, ``mul_assoc_rev, ``_root_.pow_one, - ``mul_neg, ``add_neg, ``one_smul, ``mul_smul_comm, ``Nat.algebraMap_eq_cast, - ``Int.algebraMap_eq_cast].foldlM (·.addConst ·) thms - let thms ← [``nat_rawCast_0, ``nat_rawCast_1, ``nat_rawCast_2, ``int_rawCast_neg, - ``nnrat_rawCast, ``rat_rawCast_neg].foldlM (·.addConst · (post := false)) thms - let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta } - (simpTheorems := #[thms]) - (congrTheorems := ← getSimpCongrTheorems) - pure <| ← - r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1 - -/-- Turn scalar multiplication by an explicit constant in `R` into multiplication in `A`. - -e.g. `(4 : ℚ) • x` becomes `4 * x` but `↑n • x` stays `↑n • x`. --/ -def cleanupConsts (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do - let thms : SimpTheorems := {} - let thms ← [``add_zero, ``_root_.one_mul, ``_root_.mul_one, - ``neg_mul, ``add_neg].foldlM (·.addConst ·) thms - let thms ← [``ofNat_smul, ``neg_ofNat_smul, ``neg_1_smul, ``nnRat_ofNat_smul_1, - ``nnRat_ofNat_smul_2, ``rat_ofNat_smul_1, ``rat_ofNat_smul_2 - ].foldlM (·.addConst · (post := false)) thms - let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta } - (simpTheorems := #[thms]) - (congrTheorems := ← getSimpCongrTheorems) - pure <| ← - r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1 + return (← Simp.main e ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1 /-- Collect all scalar rings from scalar multiplications using a state monad for performance. @@ -471,17 +432,17 @@ and `S` that appear are comparable, in the sense that either `R` is an `S`-algeb * `algebra with R` uses the term `R` as the scalar ring, instead of attempting to infer it automatically. - -/ +-/ elab (name := algebra) "algebra":tactic => withMainContext do - liftMetaTactic' preprocess + liftMetaTactic1 (transformAtTarget (fun e _ ↦ preprocess e) "algebra" .silent · default) let g ← getMainGoal AtomM.run .default (proveEq none g) @[tactic_alt algebra] elab (name := algebraWith) "algebra" " with " R:term : tactic => withMainContext do - liftMetaTactic' preprocess + liftMetaTactic1 (transformAtTarget (fun e _ ↦ preprocess e) "algebra" .silent · default) let ⟨u, R⟩ ← getLevelQ' (← elabTerm R none) let g ← getMainGoal AtomM.run .default (proveEq (some ⟨u, R⟩) g) diff --git a/Mathlib/Tactic/ApplyWith.lean b/Mathlib/Tactic/ApplyWith.lean index c0a6fd2370523e..d5845ff1618f8f 100644 --- a/Mathlib/Tactic/ApplyWith.lean +++ b/Mathlib/Tactic/ApplyWith.lean @@ -12,6 +12,7 @@ public meta import Lean.Elab.ConfigEval /-! # The `applyWith` tactic + The `applyWith` tactic is like `apply`, but allows passing a custom configuration to the underlying `apply` operation. -/ diff --git a/Mathlib/Tactic/Choose.lean b/Mathlib/Tactic/Choose.lean index 83a4547af5ff6f..7575ea37b1f770 100644 --- a/Mathlib/Tactic/Choose.lean +++ b/Mathlib/Tactic/Choose.lean @@ -9,6 +9,7 @@ public import Mathlib.Logic.Function.Basic /-! # `choose` tactic + Performs Skolemization, that is, given `h : ∀ a:α, ∃ b:β, p a b |- G` produces `f : α → β, hf: ∀ a, p a (f a) |- G`. diff --git a/Mathlib/Tactic/ClickSuggestions.lean b/Mathlib/Tactic/ClickSuggestions.lean index 7c6ea97903033a..9ef3d8ebeedff3 100644 --- a/Mathlib/Tactic/ClickSuggestions.lean +++ b/Mathlib/Tactic/ClickSuggestions.lean @@ -72,12 +72,13 @@ def viewKAbstractSubExpr' {m α} /-- Compute the suggestions. Use `token` for the output. -/ public def generateSuggestions (loc : SubExpr.GoalsLocation) (parentDecl? : Option Name) (token : RefreshToken) : ClickSuggestionsM Unit := withReducible do + -- Instantiate all metavariables, so that we won't need to worry about this later on. + instantiateMVarDeclMVars loc.mvarId + loc.mvarId.withContext do -- TODO: instead of just putting `✝` after inaccessible names, -- we should figure out how to use `rename_i` to actually refer to shadowed local variables. let lctx := (← getLCtx).sanitizeNames.run' { options := (← getOptions) } Meta.withLCtx' lctx do - -- Instantiate all metavariables, so that we will not need to do this later on. - instantiateMVarDeclMVars loc.mvarId trackingComputation "click_suggestions" do let (fvarId?, pos) ← match loc.loc with | .hypType fvarId pos => pure (some fvarId, pos) @@ -129,7 +130,7 @@ public def rpc (props : PanelWidgetProps) : RequestM (RequestTask Html) := let goals := if useAfter then tacticInfo.goalsAfter else tacticInfo.goalsBefore goals.contains loc.mvarId | return .text "#click_suggestions: Please reload the tactic state" - goal.ctx.val.runMetaM {} do loc.mvarId.withContext do + goal.ctx.val.runMetaM {} do withConfig Elab.Term.setElabConfig do loc.mvarId.withContext do let (statusHtml, statusToken) ← mkRefreshComponent let (solvedHtml, solvedToken) ← mkRefreshComponent let targetHtml ← diff --git a/Mathlib/Tactic/ClickSuggestions/GRewrite.lean b/Mathlib/Tactic/ClickSuggestions/GRewrite.lean index a7d91b9ac2db4c..4bff790065b76a 100644 --- a/Mathlib/Tactic/ClickSuggestions/GRewrite.lean +++ b/Mathlib/Tactic/ClickSuggestions/GRewrite.lean @@ -6,7 +6,6 @@ Authors: Jovan Gerbscheid module public import Mathlib.Tactic.ClickSuggestions.SectionState -public import Mathlib.Order.Antisymmetrization public meta import Lean.Meta.ExprLens /-! @@ -43,10 +42,11 @@ private def gcongrBackward (relName : Name) (relation : Expr) (symm : Bool) : withLocalDeclD `a α fun a ↦ do withLocalDeclD `b α fun b ↦ do withNewMCtxDepth do + let mut result : Array GrwPos := #[] -- Any relation `r` can be proved from `AntisymmRel r`, so we add this as a possible relation - let antiSymm := mkApp2 (.const ``AntisymmRel [u]) α relation - let mut result : Array GrwPos := - #[{ relName := ``AntisymmRel, relation := antiSymm, symm? := none }] + if (← getEnv).contains `AntisymmRel then + let antiSymm := mkApp2 (.const `AntisymmRel [u]) α relation + result := result.push { relName := `AntisymmRel, relation := antiSymm, symm? := none } -- If `relName` is symmetric, then include the reverse as a possible relation (`symm? := none`) let symm? ← try let dummyVar ← mkFreshExprMVar (mkApp2 relation a b) @@ -61,7 +61,9 @@ private def gcongrBackward (relName : Name) (relation : Expr) (symm : Bool) : result := result.push { relName, relation, symm? } -- For `≤`, we add the relation `<`. if relName == ``LE.le then - let (mvars, _, le) ← forallMetaTelescope (← inferType (← mkConstWithFreshMVarLevels ``le_of_lt)) + if (← getEnv).contains `le_of_lt then + let (mvars, _, le) ← + forallMetaTelescope (← inferType (← mkConstWithFreshMVarLevels `le_of_lt)) if ← isDefEq le.appFn!.appFn! relation then let lt ← instantiateMVars (← inferType mvars.back!).appFn!.appFn! result := result.push { relName := ``LT.lt, relation := lt, symm? := symm } diff --git a/Mathlib/Tactic/Common.lean b/Mathlib/Tactic/Common.lean index 6eb2bcff5c13ad..01fa2cdc218664 100644 --- a/Mathlib/Tactic/Common.lean +++ b/Mathlib/Tactic/Common.lean @@ -42,6 +42,7 @@ public import Mathlib.Tactic.Choose public import Mathlib.Tactic.ClearExclamation public import Mathlib.Tactic.ClearExcept public import Mathlib.Tactic.Clear_ +public import Mathlib.Tactic.ClickSuggestions public import Mathlib.Tactic.Coe public import Mathlib.Tactic.CongrExclamation public import Mathlib.Tactic.CongrM @@ -63,6 +64,7 @@ public import Mathlib.Tactic.Find public import Mathlib.Tactic.FunProp public import Mathlib.Tactic.GCongr public import Mathlib.Tactic.GRewrite +public import Mathlib.Tactic.GrindAttrs public import Mathlib.Tactic.GuardGoalNums public import Mathlib.Tactic.GuardHypNums public import Mathlib.Tactic.HigherOrder diff --git a/Mathlib/Tactic/ComputeDegree.lean b/Mathlib/Tactic/ComputeDegree.lean index aad91fcb2100ef..ef76af3b313f88 100644 --- a/Mathlib/Tactic/ComputeDegree.lean +++ b/Mathlib/Tactic/ComputeDegree.lean @@ -530,7 +530,7 @@ end Tactic end Mathlib.Tactic.ComputeDegree /-! - We register `compute_degree` with the `hint` tactic. - -/ +We register `compute_degree` with the `hint` tactic. +-/ register_hint 1000 compute_degree register_try?_tactic (priority := 1000) compute_degree diff --git a/Mathlib/Tactic/CongrExclamation.lean b/Mathlib/Tactic/CongrExclamation.lean index dbad9c4977c1c8..fdf409fcf2fed4 100644 --- a/Mathlib/Tactic/CongrExclamation.lean +++ b/Mathlib/Tactic/CongrExclamation.lean @@ -130,7 +130,7 @@ structure Congr!.Config where This can be used to control which side's definitions are expanded when applying the congruence lemma (if `preferLHS = true` then the RHS can be expanded). -/ preferLHS : Bool := true - /-- Allow both sides to be partial applications. + /-- Allow both sides to be partial applications, and allow overapplications. When false, given an equality `f a b = g x y z` this means we never consider proving `f a = g x y`. diff --git a/Mathlib/Tactic/Convert.lean b/Mathlib/Tactic/Convert.lean index eb74ff924a4cec..7af3d0a8cb73da 100644 --- a/Mathlib/Tactic/Convert.lean +++ b/Mathlib/Tactic/Convert.lean @@ -77,7 +77,6 @@ between `Convert.CheapConfig` and `Convert.ExpensiveConfig` based on other flags -/ structure Convert.CheapConfig extends Congr!.Config where postTransparency := .reducible - partialApp := false sameFun := true /-- Internal elaborator for `Convert.CheapConfig`: use `Convert.elabConfig` instead. -/ diff --git a/Mathlib/Tactic/CrossRefAttribute.lean b/Mathlib/Tactic/CrossRefAttribute.lean index 8b8881d079c5ef..7697769c05dbcf 100644 --- a/Mathlib/Tactic/CrossRefAttribute.lean +++ b/Mathlib/Tactic/CrossRefAttribute.lean @@ -37,20 +37,30 @@ inductive Database where | kerodon | stacks | wikidata - deriving BEq, Hashable + deriving BEq, Hashable, Ord + +namespace Database /-- The base URL for an external database's tag pages. Always ends with `/`. -/ -def databaseURL : Database → String +def url : Database → String | .kerodon => "https://kerodon.net/tag/" | .stacks => "https://stacks.math.columbia.edu/tag/" | .wikidata => "https://www.wikidata.org/wiki/" /-- The display label used in docstring links and trace output. -/ -def databaseLabel : Database → String +def label : Database → String | .kerodon => "Kerodon Tag" | .stacks => "Stacks Tag" | .wikidata => "Wikidata" +/-- A lowercase short name for the given database. Useful when exporting to JSON. -/ +def shortName : Database → String + | .kerodon => "kerodon" + | .stacks => "stacks" + | .wikidata => "wikidata" + +end Database + /-- A cross-reference from a Mathlib declaration to an entry in an external database. -/ structure Tag where /-- The name of the declaration carrying the cross-reference. -/ @@ -82,7 +92,7 @@ This is the database-agnostic core of every cross-reference attribute's `add` ha def addCrossRefDoc (db : Database) (decl : Name) (idStr comment : String) : CoreM Unit := do let oldDoc := (← findDocString? (← getEnv) decl).getD "" let commentInDoc := if comment.isEmpty then "" else s!" ({comment})" - let link := s!"[{databaseLabel db} {idStr}]({databaseURL db}{idStr}){commentInDoc}" + let link := s!"[{db.label} {idStr}]({db.url}{idStr}){commentInDoc}" addDocStringCore decl <| "\n\n".intercalate ([oldDoc, link].filter (· != "")) addTagEntry decl db idStr comment @@ -224,9 +234,9 @@ initialize Lean.registerBuiltinAttribute { name := `stacksTag descr := "Apply a Stacks or Kerodon project tag to a theorem." add := fun decl stx _attrKind => do - let (db, tag, comment) := ← match stx with - | `(attr| stacks $tag $[$comment]?) => return (Database.stacks, tag, comment) - | `(attr| kerodon $tag $[$comment]?) => return (Database.kerodon, tag, comment) + let (db, tag, comment) ← match stx with + | `(attr| stacks $tag $[$comment]?) => pure (Database.stacks, tag, comment) + | `(attr| kerodon $tag $[$comment]?) => pure (Database.kerodon, tag, comment) | _ => throwUnsupportedSyntax addCrossRefDoc db decl (← tag.getStacksTag) ((comment.map (·.getString)).getD "") -- docstrings are immutable once an asynchronous elaboration task has been started @@ -247,8 +257,8 @@ initialize Lean.registerBuiltinAttribute { name := `wikidataTag descr := "Apply a Wikidata identifier to a declaration." add := fun decl stx _attrKind => do - let (id, comment) := ← match stx with - | `(attr| wikidata $id $[$comment]?) => return (id, comment) + let (id, comment) ← match stx with + | `(attr| wikidata $id $[$comment]?) => pure (id, comment) | _ => throwUnsupportedSyntax addCrossRefDoc .wikidata decl (← id.getWikidataId) ((comment.map (·.getString)).getD "") -- docstrings are immutable once an asynchronous elaboration task has been started @@ -281,7 +291,7 @@ def traceCrossRefs (db : Database) (verbose : Bool := false) : let (parL, parR) := if d.comment.isEmpty then ("", "") else (" (", ")") let cmt := parL ++ d.comment ++ parR msgs := msgs.push - m!"[{databaseLabel db} {d.tag}]({databaseURL db ++ d.tag}) \ + m!"[{db.label} {d.tag}]({db.url ++ d.tag}) \ corresponds to declaration '{.ofConstName d.declName}'.{cmt}" if verbose then let dType := ((env.find? d.declName).getD default).type diff --git a/Mathlib/Tactic/DefEqAbuse.lean b/Mathlib/Tactic/DefEqAbuse.lean index e5f0546c1573f8..bb376b1bccf820 100644 --- a/Mathlib/Tactic/DefEqAbuse.lean +++ b/Mathlib/Tactic/DefEqAbuse.lean @@ -113,7 +113,8 @@ where return result | .ascend a? => return a?.getD empty | .compose a b => return combine (← go a) (← go b) - | .nest _ m | .group m | .tagged _ m | .withContext _ m | .withNamingContext _ m => go m + | .nest _ m | .group m | .tagged _ m | .withContext _ m | .withNamingContext _ m + | .ofOriginatingSyntax _ m => go m | .ofLazy _ _ | .ofWidget _ _ | .ofGoal _ | .ofFormatWithInfos _ => return empty /-- Convenience wrapper which accumulates the results of `visitM` across `arr`, attempting to @@ -143,6 +144,7 @@ partial def withPPOptions (msg : MessageData) (modify : Options → Options) : M | .nest n m => .nest n (withPPOptions m modify) | .group m => .group (withPPOptions m modify) | .tagged t m => .tagged t (withPPOptions m modify) + | .ofOriginatingSyntax stx m => .ofOriginatingSyntax stx (withPPOptions m modify) | .withNamingContext nc m => .withNamingContext nc (withPPOptions m modify) | .trace td header children => .trace td (withPPOptions header modify) (children.map (withPPOptions · modify)) @@ -164,19 +166,6 @@ namespace Mathlib.Tactic.DefEqAbuse unless (`Meta.isDefEq).isPrefixOf td.cls do return .descend f td header children -/-- Strip the leading status emoji that `withTraceNodeBefore` prepends to trace headers. -`withTraceNodeBefore` stores `m!"{result.toEmoji} {content}"` as the header; this strips the -emoji prefix using the structured `TraceData.result?` to know exactly what was prepended. -Needed because the same isDefEq check has different emoji prefixes across trace runs -(✅️ when it succeeds, ❌️ when it fails), but we need to compare the content. -See https://github.com/leanprover/lean4/pull/13070 for the upstream fix. -/ -private def stripHeaderEmoji (s : String) (result? : Option Lean.TraceResult) : String := - match result? with - | some result => - let emojiPrefix := s!"{result.toEmoji} " - if s.startsWith emojiPrefix then (s.drop emojiPrefix.length).toString else s - | none => s - /-- Find the deepest failing `Meta.isDefEq` trace nodes (leaf failures). Skips `onFailure` retry nodes and ignores ✅️ branches (recovered failures aren't root causes). -/ partial def findLeafFailures (msg : MessageData) : BaseIO (Array MessageData) := @@ -188,13 +177,13 @@ partial def findLeafFailures (msg : MessageData) : BaseIO (Array MessageData) := return .ascend <| if childFailures.isEmpty then #[header] else childFailures /-- Collect rendered check strings from `Meta.isDefEq` trace nodes matching a status predicate. -Returns a `HashSet` of emoji-stripped header strings. -/ +Returns a `HashSet` of header strings. -/ partial def collectIsDefEqChecks (pred : Lean.TraceResult → Bool) (msg : MessageData) : BaseIO (Std.HashSet String) := msg.visitTraceNodesM <| onlyOnDefEqNodes fun td header children => do if let some status := td.result? then if pred status then - let headerStr := stripHeaderEmoji (← header.toString) td.result? + let headerStr ← header.toString return .descend (butFirst := some {headerStr}) return .descend @@ -210,7 +199,7 @@ partial def findTransitionFailures (permSuccesses : Std.HashSet String) if permSuccesses.isEmpty then findLeafFailures msg else msg.visitTraceNodesM <| onlyOnDefEqNodes fun td header children => do unless td.result? matches some .failure do return .descend - let headerStr := stripHeaderEmoji (← header.toString) td.result? + let headerStr ← header.toString if permSuccesses.contains headerStr && !permFailures.contains headerStr then -- Transition point: fails strict, succeeds permissive, doesn't also fail permissive. -- Look for deeper transition points among children. @@ -303,17 +292,13 @@ def analyzeTraces (strictMsgs permMsgs : Array MessageData) (includeSynth : Bool return (uniqueFailures, dedupedResults) /-- Check whether a rendered isDefEq check string has syntactically identical LHS and RHS -(e.g. `"❌️ ⊤ =?= ⊤"` or `"Quiver C =?= Quiver C"`). +(e.g. `"⊤ =?= ⊤"` or `"Quiver C =?= Quiver C"`). Comparison is whitespace-insensitive to handle cases where LHS and RHS are semantically identical but rendered with different line breaks or spacing. TODO: once https://github.com/leanprover/lean4/pull/12698 is available, refactor to use `TraceData.result?` and compare the LHS/RHS `Expr`s structurally instead of string-matching. -/ def isIdenticalSidesStr (raw : String) : Bool := - if let [lhsRaw, rhs] := raw.splitOn " =?= " then - -- Strip the leading status emoji/word (first whitespace-delimited token). - let lhs := match lhsRaw.splitOn " " with - | _ :: rest => " ".intercalate rest - | _ => lhsRaw + if let [lhs, rhs] := raw.splitOn " =?= " then -- Compare up to whitespace so that line-break differences don't cause false negatives. let tokenize (s : String) : List String := (s.split Char.isWhitespace).toList.map (·.toString) |>.filter (· ≠ "") @@ -346,13 +331,14 @@ def disambiguateFailures (failures : Array MessageData) : BaseIO (Array MessageD def reportDefEqAbuse {m : Type → Type} [Monad m] [MonadLog m] [AddMessageContext m] [MonadOptions m] (kind : String) (uniqueFailures : Array MessageData) (synthResults : Array (MessageData × Array MessageData)) : m Unit := do + let failureEmoji := Lean.TraceResult.failure.toEmoji if !synthResults.isEmpty then -- Structured report: group by instance application let mut entries : Array MessageData := #[] for (app, failures) in synthResults do let failureList := joinSep - (failures.toList.map fun f => m!" {f}") "\n" - entries := entries.push m!" {app}\n{failureList}" + (failures.toList.map fun f => m!" {failureEmoji} {f}") "\n" + entries := entries.push m!" {failureEmoji} {app}\n{failureList}" let report := joinSep entries.toList "\n" logWarning m!"#defeq_abuse: {kind} fails with \ @@ -365,7 +351,7 @@ def reportDefEqAbuse {m : Type → Type} [Monad m] [MonadLog m] [AddMessageConte Could not identify specific failing isDefEq checks from traces." else let failureList := joinSep - (uniqueFailures.toList.map fun f => m!" {f}") "\n" + (uniqueFailures.toList.map fun f => m!" {failureEmoji} {f}") "\n" logWarning m!"#defeq_abuse: {kind} fails with \ `backward.isDefEq.respectTransparency true` but succeeds with `false`.\n\ diff --git a/Mathlib/Tactic/DeprecateTo.lean b/Mathlib/Tactic/DeprecateTo.lean index 6c4805d681021e..96a3d1352a2db9 100644 --- a/Mathlib/Tactic/DeprecateTo.lean +++ b/Mathlib/Tactic/DeprecateTo.lean @@ -47,11 +47,10 @@ 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 => do - return s!"{(← Std.Time.ZonedDateTime.now).toPlainDate}" - | some s => return s + let dat ← match dat with + | none => do + pure s!"{← Std.Time.PlainDate.now}" + | some s => pure s let nd := mkNode `str #[mkAtom ("\"" ++ dat.trimAsciiEnd ++ "\"")] `(command| @[deprecated (since := $nd)] alias $(mkIdent n) := $id) diff --git a/Mathlib/Tactic/DeriveEncodable.lean b/Mathlib/Tactic/DeriveEncodable.lean index 52f418e78bf039..7ac67b035ee4e9 100644 --- a/Mathlib/Tactic/DeriveEncodable.lean +++ b/Mathlib/Tactic/DeriveEncodable.lean @@ -126,7 +126,7 @@ private def S_equiv : S ≃ ℕ where · rwa [Nat.one_le_iff_ne_zero] · exact nat_unpair_lt_2 h · obtain _ | n' := n - · exact False.elim (h rfl) + · exact False.elim (h (by simp)) · have := Nat.unpair_lt (by lia : 1 ≤ n' + 1) lia diff --git a/Mathlib/Tactic/Determinant/Bird.lean b/Mathlib/Tactic/Determinant/Bird.lean new file mode 100644 index 00000000000000..05fdc2ae97903a --- /dev/null +++ b/Mathlib/Tactic/Determinant/Bird.lean @@ -0,0 +1,34 @@ +/- +Copyright (c) 2026 Paul Cadman. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Paul Cadman +-/ +module + +public import Mathlib.Tactic.Determinant.Bird.Cert + +/-! +# `norm_det` simproc and `eval_det` tactic + +A tactic for normalizing matrix determinants. +-/ + +public meta section + +open Lean Meta Elab Tactic Simp +open Mathlib.Tactic.Determinant + +/-- reify a `BirdDet` call and normalize it using the certificate-chain evaluator -/ +def normalizeBirdDet (e : Expr) : MetaM Simp.Result := do + let ⟨rα, ctx⟩ ← reifyBirdDet e + let detNorm ← certBirdDet (rα := rα) |>.run' {} |>.run ctx |>.run .reducible + Mathlib.Tactic.RingNF.cleanup {} {expr := detNorm.norm, proof? := some detNorm.proof} + +/-- Normalize a literal `birdDet` call using the certificate-chain evaluator. -/ +simproc_decl norm_det (BirdDet.birdDet _ _) := fun e => do + return .done (← normalizeBirdDet e) + +/-- Normalize `birdDet` calls in the target using the certificate-chain simproc. -/ +macro (name := evalDet) "eval_det" : tactic => `(tactic| simp only [norm_det]) + +end diff --git a/Mathlib/Tactic/Determinant/Bird/Cert.lean b/Mathlib/Tactic/Determinant/Bird/Cert.lean new file mode 100644 index 00000000000000..e92e0829b6d1ad --- /dev/null +++ b/Mathlib/Tactic/Determinant/Bird/Cert.lean @@ -0,0 +1,412 @@ +/- +Copyright (c) 2026 Paul Cadman. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Paul Cadman +-/ +module + +public meta import Mathlib.Tactic.Determinant.Bird.Meta +public meta import Mathlib.Tactic.Ring + +/-! + +# Certificate-chain evaluator for `BirdDet.birdDet` + +This file contains an evaulator that computes the ring tactic normal form of +`Mathlib.LinearAlgebra.Matrix.Determinant.Bird.birdDet` via iteratively +unfolding its definition, using the ring tactic for ring operations, and caching +intermediate certificates. + +The structure `Cert rα` carries the proof certificate and the evaluator builds +larger certificates as `birdDet` is unfolded. + +The entrypoint of the evaluator `certBirdDet` follows the two branches (n=0, +n=k+1) of the `birdDet` function: + +``` +certBirdDet (birdDet n A) + n = 0: + birdDet n A + = 1 -- via BirdDet.birdDet_zero + = ring normal form of 1 -- via certEval + n = k + 1: + birdDet n A + = (-1)^k * iter n A k (get n A) 0 0 -- via BirdDet.birdDet_eq + = ring normal form of the product -- certMul (certBirdSign k) (certIter k 0 0) +``` + +The `iter n A k (get n A) i j` function branches on k, (k=0, k=t+1) and +therefore the `certIter` function has two branches: + +``` +certIter k i j + k = 0: + iter n A 0 F i j = F i j -- via BirdDet.iter_zero + = ring normal form of A[i][j] -- via certEntry i j + k = t + 1: + iter n A (t + 1) F i j + = -(sumFrom n (i + 1) fun k => iter n A t F k k) * get n A i j + + sumFrom n (i + 1) fun k => iter n A t F i k * get n A k j + -- via BirdDet.iter_succ + = normal form of the first summand + normal form of the second summand + -- via certAdd + (certMul (certNeg (certDiag t (i + 1))) (certEntry i j)) + (certTail t i j (i + 1)) +``` + +Then the `certDiag` and `certTail` functions certify the two kinds of `sumFrom` +expressions. + +The evaulator also memoizes the `certIter`, `certDiag` and `certEntry` functions +to improve performance. + +## Main definitions + +- `certEntry` certifies `BirdDet.get`. +- `certSumFromStop` and `certSumFromStep` certifies `BirdDet.sumFrom_stop` and + `BirdDet.sumFrom_step`. +- `certIter` certifies `BirdDet.iter_zero` and `BirdDet.iter_succ`. +- `certBirdDet` certifies `BirdDet.birdDet_zero` and `BirdDet.birdDet_eq`. +-/ + +public meta section + +open Lean Meta Qq +open Mathlib.Tactic.Ring + +variable {u : Level} {α : Q(Type u)} {rα : Q(CommRing $α)} + +namespace Mathlib.Tactic.Determinant + +/-- The ring tactic normal-form value. -/ +abbrev CertVal {u : Level} {α : Q(Type u)} + (rα : Q(CommRing $α)) (e : Q($α)) := + Common.ExSum RatCoeff (commSemiringOfCommRing rα) e + +/-- The ring tactic result carried by a certificate. -/ +abbrev CertResult {u : Level} {α : Q(Type u)} + (rα : Q(CommRing $α)) (subject : Q($α)) := + Common.Result (CertVal rα) subject + +namespace Ctx + +/-- Return an expression for the partially applied function `iter n A t (get n A)` -/ +def iterP (ctx : Ctx rα) (t : ℕ) : Q(ℕ → ℕ → $α) := + let dim : Q(ℕ) := ctx.dimensionLit + let A : Q(Array $α) := ctx.arrayExpr + q(BirdDet.iter $dim $A $t (BirdDet.get $dim $A)) + +/-- Return an expression `sumFrom n lo f` -/ +def sumFrom (ctx : Ctx rα) (lo : ℕ) (f : Q(ℕ → $α)) : Q($α) := + let dim : Q(ℕ) := ctx.dimensionLit + q(BirdDet.sumFrom $dim $lo $f) + +end Ctx + +/-- A certificate that proves `subject = result.norm` via `result.proof` -/ +structure Cert {u : Level} {α : Q(Type u)} (rα : Q(CommRing $α)) where + /-- The expression being certified. -/ + {subject : Q($α)} + /-- The result of evaluating `subject` using the ring tactic. -/ + result : CertResult rα subject + /-- `true` when `norm` is zero, used as a hint to the evaluator. -/ + isZero : Bool + +namespace Cert + +variable + {u : Level} + {α : Q(Type u)} + {rα : Q(CommRing $α)} + +/-- The ring tactic normal form of `c.subject` -/ +def norm (c : Cert rα) : Q($α) := + c.result.expr + +/-- The internal ring tactic representation of `c.norm` -/ +def val (c : Cert rα) : CertVal rα c.norm := + c.result.val + +/-- The proof that `c.subject = c.norm` -/ +def proof (c : Cert rα) : Q($c.subject = $c.norm) := + c.result.proof + +/-- Prepend an equality to an existing normalized certificate. + +Given `c.proof : s.subject = c.norm` and `h : lhs = s.subject` return a +certificate with `proof : lhs = c.norm`. +-/ +def chainProof {lhs rhs : Q($α)} (c : Cert rα) (h : Q($lhs = $rhs)) : Cert rα := + have : $rhs =Q $c.subject := ⟨⟩ + let hProof : Q($lhs = $c.subject) := h + let proof : Q($lhs = $c.norm) := q(Eq.trans $hProof $c.proof) + { c with + subject := lhs + result.proof := proof + } + +end Cert + +/-- Cache certificates that are reused by the recursive Bird evaluator. -/ +structure CertCache {u : Level} {α : Q(Type u)} (rα : Q(CommRing $α)) where + /-- Cache for entry certificates, keyed by matrix indices. -/ + entryCache : Std.HashMap (ℕ × ℕ) (Cert rα) := {} + /-- Cache for `iter` certificates, keyed by recursion index and matrix indices. -/ + iterCache : Std.HashMap (ℕ × ℕ × ℕ) (Cert rα) := {} + /-- Cache for diagonal-tail certificates, keyed by recursion index and lower bound. -/ + diagCache : Std.HashMap (ℕ × ℕ) (Cert rα) := {} + +/-- The monad used by the certificate-chaining evaluator -/ +abbrev CertM {u : Level} {α : Q(Type u)} (rα : Q(CommRing $α)) := + StateT (CertCache rα) (ReaderT (Ctx rα) AtomM) + +/-- Checks if `val` is zero according to the ring tactic -/ +def isZeroVal {e : Q($α)} (val : CertVal rα e) : Bool := + match val with + | .zero => true + | .add .. => false + +/-- Construct a `Cert rα` from a ring tactic result -/ +def toCert {e : Q($α)} (res : Common.Result (CertVal rα) e) : Cert rα := + { result := res + isZero := isZeroVal res.val } + +/-- Build a zero certificate from a proof `lhs = 0`. -/ +def zeroCertOfProof {lhs : Q($α)} (h : Q($lhs = 0)) : Cert rα where + result.expr := q(0) + result.val := .zero + result.proof := h + isZero := true + +/-- If c.norm = 0, return a certificate with proof `x * c.subject = 0` without +recursively certifying `x`. -/ +def zeroProdCert (x : Q($α)) (c : Cert rα) : + MetaM (Cert rα) := do + let zero : Q($α) := q(0) + have : $c.norm =Q $zero := ⟨⟩ + let h : Q($x * $c.subject = $x * $zero) := + q(congrArg (fun y => $x * y) $c.proof) + return zeroCertOfProof q(Eq.trans $h (mul_zero $x)) + +/-- Certify `e = norm` by evaluating `e` with the `ring` normalizer. -/ +def certEval (e : Q($α)) : CertM rα (Cert rα) := do + let ctx ← read + let res ← Common.eval rcℕ ctx.rc ctx.cα e + return toCert res + +/-- Certify `a.subject + b.subject` from certificates for `a` and `b`. -/ +def certAdd (a b : Cert rα) : CertM rα (Cert rα) := do + let ctx ← read + let c ← toCert <$> Common.evalAdd ctx.rc rcℕ a.val b.val + let h : Q($a.subject + $b.subject = $a.norm + $b.norm) := + q(congr (congrArg (fun x y => x + y) $a.proof) $b.proof) + return c.chainProof h + +/-- Certify `a.subject * b.subject` from certificates for `a` and `b`. -/ +def certMul (a b : Cert rα) : CertM rα (Cert rα) := do + let ctx ← read + let c ← toCert <$> Common.evalMul ctx.rc rcℕ a.val b.val + let h : Q($a.subject * $b.subject = $a.norm * $b.norm) := + q(congr (congrArg (fun x y => x * y) $a.proof) $b.proof) + return c.chainProof h + +/-- Certify `-a.subject` from a certificate for `a`. -/ +def certNeg (a : Cert rα) : CertM rα (Cert rα) := do + let ctx ← read + let c ← toCert <$> Common.evalNeg ctx.rc rα a.val + let h : Q(-$a.subject = -$a.norm) := + q(congrArg (fun x => -x) $a.proof) + return c.chainProof h + +/-- Certify the sign factor `(-1)^k` from `BirdDet.birdDet_eq`. -/ +def certBirdSign (k : ℕ) : CertM rα (Cert rα) := do + certEval q((-1 : $α) ^ $k) + +/-- Certify one matrix entry lookup `BirdDet.get n A i j`. -/ +def certEntry (i j : ℕ) : CertM rα (Cert rα) := do + if let some c := (← get).entryCache[(i, j)]? then + return c + let ctx ← read + let {dimension := dim, dimensionLit := dimLit, arrayExpr := A, arrayEntries, ..} := ctx + let lhs : Q($α) := q(BirdDet.get $dimLit $A $i $j) + -- The index of the matrix entry (i, j) in arrayEntries + let idx := i * dim + j + let entry := arrayEntries.getD idx q(0) + let ce ← certEval entry + have : $lhs =Q $entry := ⟨⟩ + let h : Q($lhs = $entry) := q(rfl) + let cert := ce.chainProof h + modify fun s => {s with entryCache := s.entryCache.insert (i, j) cert} + return cert + +/-- Certify the stop branch of `BirdDet.sumFrom`. + +This corresponds to the `else 0` branch of: + +``` +sumFrom n lo f = if lo < n then f lo + sumFrom n (lo + 1) f else 0 +``` + +Throws a meta-level error if called with `lo` such that `lo < ctx.dimension`. +-/ +def certSumFromStop (lo : ℕ) (f : Q(ℕ → $α)) : CertM rα (Cert rα) := do + let ctx ← read + if lo < ctx.dimension then + throwError "certSumFromStop called with {lo} such that {lo} < {ctx.dimension}" + have dimLit : Q(ℕ) := ctx.dimensionLit + let hNot : Q(¬ $lo < $dimLit) ← mkDecideProofQ q(¬ $lo < $dimLit) + return zeroCertOfProof q(BirdDet.sumFrom_stop $dimLit $lo $f $hNot) + +/-- Certify the step branch of `BirdDet.sumFrom`. + +This corresponds to the `lo < n` branch of: + +``` +sumFrom n lo f = if lo < n then f lo + sumFrom n (lo + 1) f else 0 +``` + +Throws a meta-level error if called with `lo` such that `¬ lo < ctx.dimension`. +-/ +def certSumFromStep + (lo : ℕ) (f : Q(ℕ → $α)) + (headCert tailCert : CertM rα (Cert rα)) : CertM rα (Cert rα) := do + let ctx ← read + unless lo < ctx.dimension do + throwError "certSumFromStep called with {lo} such that ¬ {lo} < {ctx.dimension}" + have dim : Q(ℕ) := ctx.dimensionLit + let hLt : Q($lo < $dim) ← mkDecideProofQ q($lo < $dim) + let sumCert ← certAdd (← headCert) (← tailCert) + return sumCert.chainProof q(BirdDet.sumFrom_step $dim $lo $f $hLt) + +mutual + +/-- Certify a `BirdDet.iter` call. -/ +partial def certIter (t i j : ℕ) : CertM rα (Cert rα) := do + if let some c := (← get).iterCache[(t, i, j)]? then + return c + let ctx ← read + let {dimensionLit := dimLit, arrayExpr := A, ..} := ctx + let cert ← match t with + -- The t=0 branch of `BirdDet.iter`, unfold using `BirdDet.iter_zero` + | 0 => do + let ce ← certEntry i j + let h := q(BirdDet.iter_zero $dimLit $A (BirdDet.get $dimLit $A) $i $j) + pure (ce.chainProof h) + -- The t=t+1 branch of `BirdDet.iter`, unfold using `BirdDet.iter_succ` + | t' + 1 => do + -- First summand in `BirdDet.iter_succ`: + -- -(sumFrom n (i + 1) fun k => F_t k k) * get n A i j + let diagSummand := q(fun k => $(ctx.iterP t') k k) + let negDiagSum := q(-$(ctx.sumFrom (i + 1) diagSummand)) + let entryCert ← certEntry i j + let diagProdCert ← + -- If `get n A i j = 0` then we can skip computation of + -- `-(sumFrom n (i + 1) fun k => F_t k k)` + if entryCert.isZero then + zeroProdCert negDiagSum entryCert + else do + let diagSumCert ← certDiag t' (i + 1) + let negDiagSumCert ← certNeg diagSumCert + certMul negDiagSumCert entryCert + -- Second summand in `BirdDet.iter_succ`: + -- sumFrom n (i + 1) fun k => F_t i k * get n A k j + let tailSumCert ← certTail t' i j (i + 1) + let rhsCert ← certAdd diagProdCert tailSumCert + let h := q(BirdDet.iter_succ $dimLit $A $t' (BirdDet.get $dimLit $A) $i $j) + pure (rhsCert.chainProof h) + modify fun s => {s with iterCache := s.iterCache.insert (t, i, j) cert} + return cert + + +/-- Certify the diagonal tail sum from `BirdDet.iter_succ`: + +``` +sumFrom n (i + 1) fun k => iter n A t F k k) +``` +-/ +partial def certDiag (t lo : ℕ) : CertM rα (Cert rα) := do + if let some c := (← get).diagCache[(t, lo)]? then + return c + let ctx ← read + let diagonalSummand := q(fun k => $(ctx.iterP t) k k) + let cert ← + if lo < ctx.dimension + then do + let headCert := certIter t lo lo + let tailCert := certDiag t (lo + 1) + certSumFromStep + lo + diagonalSummand + headCert + tailCert + else + certSumFromStop lo diagonalSummand + modify fun s => {s with diagCache := s.diagCache.insert (t, lo) cert} + return cert + +/-- Certify the upper-tail sum from `BirdDet.iter_succ`: + +``` +sumFrom n (i + 1) fun k => iter n A t F i k * get n A k j +``` +-/ +partial def certTail (t i j lo : ℕ) : CertM rα (Cert rα) := do + let ctx ← read + let {dimensionLit := dimLit, arrayExpr := A, ..} := ctx + let tailSummand := + q(fun k => + $(ctx.iterP t) $i k * + BirdDet.get $dimLit $A k $j) + if lo < ctx.dimension + then do + -- headCert certifies `iter n A t F i lo * get n A lo j` + let headCert := do + let entryCert ← certEntry lo j + -- If `get n A lo j = 0` then we can skip computation of + -- `iter n A t F i lo` + if entryCert.isZero + then + zeroProdCert + q($(ctx.iterP t) $i $lo) + entryCert + else do + let iterCert ← certIter t i lo + certMul iterCert entryCert + let tailCert := certTail t i j (lo + 1) + certSumFromStep + lo + tailSummand + headCert + tailCert + else + certSumFromStop lo tailSummand + +end + +/-- Certify a `BirdDet.birdDet n A` call. -/ +def certBirdDet : CertM rα (Cert rα) := do + let ctx ← read + let {dimension := dim, dimensionLit := dimLit, arrayExpr, ..} := ctx + if dim == 0 + then + let ce ← certEval q(1 : $α) + have : $dimLit =Q 0 := ⟨⟩ + have A : Q(Array $α) := arrayExpr + let h := q(BirdDet.birdDet_zero $A) + return ce.chainProof h + else + -- The non-zero `BirdDet.birdDet_eq` branch matches `k + 1` + -- so we set k := `ctx.dimension - 1`. + let k := dim - 1 + let cs ← certBirdSign k + let ci ← certIter k 0 0 + let cm ← certMul cs ci + have kLit := mkNatLitQ k + have : $dimLit =Q $kLit + 1 := ⟨⟩ + let hn : Q($dimLit = $kLit + 1) := q(rfl) + let h := q(BirdDet.birdDet_eq $dimLit $kLit $arrayExpr $hn) + return cm.chainProof h + +end Mathlib.Tactic.Determinant + +end diff --git a/Mathlib/Tactic/Determinant/Bird/Meta.lean b/Mathlib/Tactic/Determinant/Bird/Meta.lean new file mode 100644 index 00000000000000..2b16374f8da6c0 --- /dev/null +++ b/Mathlib/Tactic/Determinant/Bird/Meta.lean @@ -0,0 +1,118 @@ +/- +Copyright (c) 2026 Paul Cadman. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Paul Cadman +-/ +module + +public import Mathlib.LinearAlgebra.Matrix.Determinant.Bird +public meta import Mathlib.Tactic.Ring +public meta import Mathlib.Util.Qq + +/-! +# Reification support for the determinant tactic + +This file contains the meta-level parser, `refiyBirdDet`, used by +`normalizeBirdDet` to turn `BirdDet.birdDet` calls into the context used by the +certificate-chain evaluator. + +## Main definitions + +- `reifyBirdDet`: Parse a call to `BirdDet.birdDet`. + +-/ + +public meta section + +open Lean Meta Qq +open Mathlib.Tactic.Ring + +namespace Mathlib.Tactic.Determinant + +/-- Construct a `CommSemiring` instance expression from a `CommRing` instance expression -/ +abbrev commSemiringOfCommRing {u : Level} {α : Q(Type u)} + (rα : Q(CommRing $α)) : Q(CommSemiring $α) := + q(CommRing.toCommSemiring (α := $α) (s := $rα)) + +/-- Parse an array literal into an array of element expressions. + +Compared to `getArrayLit?`, this also performs `whnf`. +-/ +def arrayLiteral? (e : Expr) : MetaM (Option (Array Expr)) := do + if let some elems ← getArrayLit? e then return some elems + let e ← whnf e + match_expr e with + | Array.mk _ xs => getListLit? xs + | _ => return none + +/-- The context for a certificate evaluation. -/ +structure Ctx {u : Level} {α : Q(Type u)} (rα : Q(CommRing $α)) where + /-- `Ring` evaluation cache for the scalar ring. -/ + cα : Common.Cache (commSemiringOfCommRing rα) + /-- Proof-producing ring arithmetic. -/ + rc : Common.RingCompute RatCoeff (commSemiringOfCommRing rα) + /-- The dimension of the reified matrix -/ + dimension : ℕ + /-- The quoted dimension expression from the reified determinant call. -/ + dimensionLit : Q(ℕ) + /-- The array of matrix entries as an Expr -/ + arrayExpr : Q(Array $α) + /-- An array of matrix entry `Expr`s` -/ + arrayEntries : Array Q($α) + +/-- The ring instance and evaluator context parsed by `reifyBirdDet`. -/ +structure ReifiedBirdDet where + /-- The universe level associated with the `birdDet` call -/ + {u : Level} + /-- The type of a matrix entry -/ + {α : Q(Type u)} + /-- The `CommRing` instance for matrix entries -/ + rα : Q(CommRing $α) + /-- The evaluator context for the parsed determinant expression. -/ + ctx : Ctx rα + +/-- Recognise a `birdDet` call and reify it into an evaluator context. -/ +def reifyBirdDet (e : Expr) : MetaM ReifiedBirdDet := do + let e ← instantiateMVars e + let ⟨_, α, _⟩ ← inferTypeQ' e + let_expr BirdDet.birdDet _ birdRingInst dimensionExpr arrayExpr := e + | throwError "expected an application of `birdDet, got {e}" + let some rα ← checkTypeQ birdRingInst q(CommRing $α) + | throwError "expected `birdDet` ring instance to have type {q(CommRing $α)}" + let dimensionExpr ← whnf dimensionExpr + let some dimensionLit ← checkTypeQ dimensionExpr q(ℕ) + | throwError "expected the dimension to have type `ℕ`, got {dimensionExpr}" + let some dimension ← getNatValue? dimensionLit + | throwError "expected the dimension to be a `ℕ` literal, got {dimensionLit}" + let some arrayExpr ← checkTypeQ arrayExpr q(Array $α) + | throwError "expected the array to have type {q(Array $α)}" + let some arrayEntries ← arrayLiteral? arrayExpr + | throwError "expected an array literal matrix, got {arrayExpr}" + unless arrayEntries.size == dimension * dimension do + throwError "matrix size mismatch: array has {arrayEntries.size} entries, \ + expected {dimension * dimension}" + let arrayEntries ← arrayEntries.mapM fun entry => do + let some entry ← checkTypeQ entry α + | throwError "expected array entry to have type {α}" + return entry + let sα := commSemiringOfCommRing rα + let cα : Common.Cache sα := { + rα := some rα + dsα := none + czα := none + } + return { + rα + ctx := { + cα + rc := ringCompute cα + dimension + dimensionLit + arrayExpr + arrayEntries + } + } + +end Mathlib.Tactic.Determinant + +end diff --git a/Mathlib/Tactic/ExistsI.lean b/Mathlib/Tactic/ExistsI.lean index 0b988ef9d731a2..c2e9e49a513c80 100644 --- a/Mathlib/Tactic/ExistsI.lean +++ b/Mathlib/Tactic/ExistsI.lean @@ -8,6 +8,7 @@ module public import Mathlib.Init /-! # The `existsi` tactic + This file defines the `existsi` tactic: its purpose is to instantiate existential quantifiers. Internally, it applies the `refine` tactic. -/ diff --git a/Mathlib/Tactic/FieldSimp.lean b/Mathlib/Tactic/FieldSimp.lean index 4848295ba6a635..9ca4f39b9d0c0a 100644 --- a/Mathlib/Tactic/FieldSimp.lean +++ b/Mathlib/Tactic/FieldSimp.lean @@ -62,7 +62,7 @@ def onExponent (l : qNF M) (f : ℤ → ℤ) : qNF M := /-- Build a transparent expression for the product of powers represented by `l : qNF M`. -/ def evalPrettyMonomial (iM : Q(GroupWithZero $M)) (r : ℤ) (x : Q($M)) : - MetaM (Σ e : Q($M), Q(zpow' $x $r = $e)) := do + MetaM (Σ e : Q($M), Q(zpow' $x $r = $e)) := match r with | 0 => /- If an exponent is zero then we must not have been able to prove that x is nonzero. -/ return ⟨q($x / $x), q(zpow'_zero_eq_div ..)⟩ @@ -111,10 +111,10 @@ def removeZeros of) the negative powers. -/ def split (iM : Q(CommGroupWithZero $M)) (l : qNF M) : MetaM (Σ l_n l_d : qNF M, Q(NF.eval $(l.toNF) - = NF.eval $(l_n.toNF) / NF.eval $(l_d.toNF))) := do + = NF.eval $(l_n.toNF) / NF.eval $(l_d.toNF))) := match l with | [] => return ⟨[], [], q(Eq.symm (div_one (1:$M)))⟩ - | ((r, x), i) :: t => + | ((r, x), i) :: t => do let ⟨t_n, t_d, pf⟩ ← split iM t if r > 0 then return ⟨((r, x), i) :: t_n, t_d, (q(NF.cons_eq_div_of_eq_div $r $x $pf):)⟩ @@ -125,13 +125,13 @@ def split (iM : Q(CommGroupWithZero $M)) (l : qNF M) : return ⟨t_n, ((r', x), i) :: t_d, (q(NF.cons_eq_div_of_eq_div' $r' $x $pf):)⟩ private def evalPrettyAux (iM : Q(CommGroupWithZero $M)) (l : qNF M) : - MetaM (Σ e : Q($M), Q(NF.eval $(l.toNF) = $e)) := do + MetaM (Σ e : Q($M), Q(NF.eval $(l.toNF) = $e)) := match l with | [] => return ⟨q(1), q(rfl)⟩ - | [((r, x), _)] => + | [((r, x), _)] => do let ⟨e, pf⟩ ← evalPrettyMonomial q(inferInstance) r x return ⟨e, q(by rw [NF.eval_cons]; exact Eq.trans (one_mul _) $pf)⟩ - | ((r, x), k) :: t => + | ((r, x), k) :: t => do let ⟨e, pf_e⟩ ← evalPrettyMonomial q(inferInstance) r x let ⟨t', pf⟩ ← evalPrettyAux iM t have pf'' : Q(NF.eval $(qNF.toNF (((r, x), k) :: t)) = (NF.eval $(qNF.toNF t)) * zpow' $x $r) := @@ -144,7 +144,7 @@ def evalPretty (iM : Q(CommGroupWithZero $M)) (l : qNF M) : let ⟨l_n, l_d, pf⟩ ← split iM l let ⟨num, pf_n⟩ ← evalPrettyAux q(inferInstance) l_n let ⟨den, pf_d⟩ ← evalPrettyAux q(inferInstance) l_d - match l_d with + match (dependent := true) l_d with | [] => return ⟨num, q(eq_div_of_eq_one_of_subst $pf $pf_n)⟩ | _ => let pf_n : Q(NF.eval $(l_n.toNF) = $num) := pf_n @@ -281,14 +281,14 @@ def mkDenomConditionProofSucc {iM : Q(CommGroupWithZero $M)} (disch : ∀ {u : Level} (type : Q(Sort u)), MetaM Q($type)) {cond : DenomCondition (M := M) q(inferInstance)} {L : qNF M} (hL : cond.proof L) (e : Q($M)) (r : ℤ) (i : ℕ) : - MetaM (Q($e ≠ 0) × cond.proof (((r, e), i) :: L)) := do + MetaM (Q($e ≠ 0) × cond.proof (((r, e), i) :: L)) := match cond with | .none => return (← disch q($e ≠ 0), Unit.unit) - | .nonzero => + | .nonzero => do let pf ← disch q($e ≠ 0) let pf₀ : Q(NF.eval $(qNF.toNF L) ≠ 0) := hL return (pf, q(NF.cons_ne_zero $r $pf $pf₀)) - | .positive _ _ _ _ => + | .positive _ _ _ _ => do let pf ← disch q(0 < $e) let pf₀ : Q(0 < NF.eval $(qNF.toNF L)) := hL let pf' := q(NF.cons_pos $r (x := $e) $pf $pf₀) @@ -301,14 +301,14 @@ def mkDenomConditionProofSucc' {iM : Q(CommGroupWithZero $M)} (disch : ∀ {u : Level} (type : Q(Sort u)), MetaM Q($type)) {cond : DenomCondition (M := M) q(inferInstance)} {L : qNF M} (hL : cond.proof L) (e : Q($M)) (r : ℤ) (i : ℕ) : - MetaM (cond.proof (((r, e), i) :: L)) := do + MetaM (cond.proof (((r, e), i) :: L)) := match cond with | .none => return Unit.unit - | .nonzero => + | .nonzero => do let pf ← disch q($e ≠ 0) let pf₀ : Q(NF.eval $(qNF.toNF L) ≠ 0) := hL return q(NF.cons_ne_zero $r $pf $pf₀) - | .positive _ _ _ _ => + | .positive _ _ _ _ => do let pf ← disch q(0 < $e) let pf₀ : Q(0 < NF.eval $(qNF.toNF L)) := hL return q(NF.cons_pos $r (x := $e) $pf $pf₀) @@ -799,7 +799,7 @@ simproc_decl fieldLt (LT.lt _ _) := FieldSimp.proc attribute [field, inherit_doc FieldSimp.proc] fieldEq fieldLe fieldLt /-! - We register `field_simp` with the `hint` tactic. - -/ +We register `field_simp` with the `hint` tactic. +-/ register_hint 1000 field_simp register_try?_tactic (priority := 1000) field_simp diff --git a/Mathlib/Tactic/FieldSimp/Attr.lean b/Mathlib/Tactic/FieldSimp/Attr.lean index b43d38f171c369..a8d460480067d9 100644 --- a/Mathlib/Tactic/FieldSimp/Attr.lean +++ b/Mathlib/Tactic/FieldSimp/Attr.lean @@ -15,5 +15,5 @@ open Lean Meta /-- Initialize the attribute `field` grouping the simprocs associated to the `field_simp` tactic. -/ initialize fieldSimpExt : Simp.SimprocExtension - ← Simp.registerSimprocAttr `field + ← Simp.registerSimprocAttr `field "Attribute grouping the simprocs associated to the field_simp tactic" none diff --git a/Mathlib/Tactic/FieldSimp/Lemmas.lean b/Mathlib/Tactic/FieldSimp/Lemmas.lean index 9b5669aa6f68cb..177257c99d0d68 100644 --- a/Mathlib/Tactic/FieldSimp/Lemmas.lean +++ b/Mathlib/Tactic/FieldSimp/Lemmas.lean @@ -398,7 +398,7 @@ def Sign.expr : Sign M → Q($M) → Q($M) the product with `c` of (± `y`) (here taking the specified sign) is ± `c * y`. -/ def Sign.mulRight (iM : Q(CommGroupWithZero $M)) (c y : Q($M)) (g : Sign M) : MetaM Q($(g.expr q($c * $y)) = $c * $(g.expr y)) := do - match g with + match (dependent := true) g with | .plus => pure q(rfl) | .minus _ => assumeInstancesCommute @@ -409,7 +409,7 @@ the product of (± `y₁`) and (± `y₂`) (here taking the specified signs) is proof and the computed sign. -/ def Sign.mul (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sign M) : MetaM (Σ (G : Sign M), Q($(g₁.expr y₁) * $(g₂.expr y₂) = $(G.expr q($y₁ * $y₂)))) := do - match g₁, g₂ with + match (dependent := true) g₁, g₂ with | .plus, .plus => pure ⟨.plus, q(rfl)⟩ | .plus, .minus i => assumeInstancesCommute @@ -425,7 +425,7 @@ def Sign.mul (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sig the inverse of (± `y`) (here taking the specified sign) is ± `y⁻¹`. -/ def Sign.inv (iM : Q(CommGroupWithZero $M)) (y : Q($M)) (g : Sign M) : MetaM (Q($(g.expr y)⁻¹ = $(g.expr q($y⁻¹)))) := do - match g with + match (dependent := true) g with | .plus => pure q(rfl) | .minus _ => assumeInstancesCommute @@ -436,7 +436,7 @@ the quotient of (± `y₁`) and (± `y₂`) (here taking the specified signs) is proof and the computed sign. -/ def Sign.div (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sign M) : MetaM (Σ (G : Sign M), Q($(g₁.expr y₁) / $(g₂.expr y₂) = $(G.expr q($y₁ / $y₂)))) := do - match g₁, g₂ with + match (dependent := true) g₁, g₂ with | .plus, .plus => pure ⟨.plus, q(rfl)⟩ | .plus, .minus i => assumeInstancesCommute @@ -452,7 +452,7 @@ def Sign.div (iM : Q(CommGroupWithZero $M)) (y₁ y₂ : Q($M)) (g₁ g₂ : Sig the negation of (± `y`) (here taking the specified sign) is ∓ `y`. -/ def Sign.neg (iM : Q(Field $M)) (y : Q($M)) (g : Sign M) : MetaM (Σ (G : Sign M), Q(-$(g.expr y) = $(G.expr y))) := do - match g with + match (dependent := true) g with | .plus => pure ⟨.minus iM, q(rfl)⟩ | .minus _ => assumeInstancesCommute @@ -463,7 +463,7 @@ the exponentiation to power `s : ℕ` of (± `y`) (here taking the specified sig return this proof and the computed sign. -/ def Sign.pow (iM : Q(CommGroupWithZero $M)) (y : Q($M)) (g : Sign M) (s : ℕ) : MetaM (Σ (G : Sign M), Q($(g.expr y) ^ $s = $(G.expr q($y ^ $s)))) := do - match g with + match (dependent := true) g with | .plus => pure ⟨.plus, q(rfl)⟩ | .minus i => assumeInstancesCommute @@ -479,7 +479,7 @@ the exponentiation to power `s : ℤ` of (± `y`) (here taking the specified sig return this proof and the computed sign. -/ def Sign.zpow (iM : Q(CommGroupWithZero $M)) (y : Q($M)) (g : Sign M) (s : ℤ) : MetaM (Σ (G : Sign M), Q($(g.expr y) ^ $s = $(G.expr q($y ^ $s)))) := do - match g with + match (dependent := true) g with | .plus => pure ⟨.plus, q(rfl)⟩ | .minus i => assumeInstancesCommute diff --git a/Mathlib/Tactic/Finiteness.lean b/Mathlib/Tactic/Finiteness.lean index 73551303bf70ec..40cc18f28f2c71 100644 --- a/Mathlib/Tactic/Finiteness.lean +++ b/Mathlib/Tactic/Finiteness.lean @@ -111,7 +111,7 @@ macro_rules `(tactic| · ($[have := $h];*); finiteness_nonterminal $c*) /-! - We register `finiteness` with the `hint` tactic. - -/ +We register `finiteness` with the `hint` tactic. +-/ register_hint 1000 finiteness register_try?_tactic (priority := 1000) finiteness diff --git a/Mathlib/Tactic/GCongr/Core.lean b/Mathlib/Tactic/GCongr/Core.lean index 9a3592bba6c1e7..82658fddf1dd60 100644 --- a/Mathlib/Tactic/GCongr/Core.lean +++ b/Mathlib/Tactic/GCongr/Core.lean @@ -144,7 +144,7 @@ public meta section namespace Mathlib.Tactic.GCongr open Lean Meta -/-- `GCongrKey` is the key used in the hashmap for looking up `gcongr` lemmas. -/ +/-- `GCongrKey` is the key used in the dictionary for looking up `gcongr` lemmas. -/ structure GCongrKey where /-- The name of the relation. For example, `a + b ≤ a + c` has ``relName := `LE.le``. -/ relName : Name @@ -176,8 +176,9 @@ structure GCongrHyp where /-- Structure recording the data for a "generalized congruence" (`gcongr`) lemma. -/ structure GCongrLemma where - /-- The key under which the lemma is stored. -/ - key : GCongrKey + /-- The keys under which the lemma is stored. This is usually one key, + but for `gcongr strict` lemmas, this stores two keys, for the LHS and RHS respectively. -/ + keys : List GCongrKey /-- The name of the lemma. -/ declName : Name /-- `mainSubgoals` are the subgoals on which `gcongr` will be recursively called. -/ @@ -202,11 +203,11 @@ abbrev GCongrLemmas := Std.TreeMap GCongrKey (List GCongrLemma) def GCongrLemma.prioLE (a b : GCongrLemma) : Bool := (compare a.prio b.prio).then (compare b.numVarying a.numVarying) |>.isLE -/-- Insert a `GCongrLemma` in a collection of lemmas, making sure that the lemmas are sorted. -/ +/-- Insert a `GCongrLemma` in a collection of lemmas, making sure they are sorted by priority. -/ def addGCongrLemmaEntry (m : GCongrLemmas) (l : GCongrLemma) : GCongrLemmas := - m.alter l.key fun - | none => [l] - | some es => insert l es + l.keys.foldl (init := m) fun m key ↦ m.alter key fun + | none => [l] + | some es => insert l es where /--- Insert a `GCongrLemma` in the correct place in a list of lemmas. -/ insert (l : GCongrLemma) : List GCongrLemma → List GCongrLemma @@ -220,6 +221,27 @@ initialize gcongrExt : SimpleScopedEnvExtension GCongrLemma GCongrLemmas ← initial := {} } +/-- Return the `gcongr` lemmas whose conclusion has the shape `relName (lhs ..) (rhs ..)`, +where `arity` is the number of arguments in `..`. This is used by the `gcongr` tactic. -/ +def findGCongrLemmas? (relName lhs rhs : Name) (arity : Nat) : CoreM (List GCongrLemma) := do + let lemmas := gcongrExt.getState (← getEnv) + let key := { relName, head := lhs, arity } + let some lemmas := lemmas.get? key | return [] + let keys := if lhs == rhs then [key] else [key, { key with head := rhs }] + return lemmas.filter (·.keys == keys) + +/-- Get the `gcongr` lemmas whose conclusion has the shape +`relName (head ..) _`, if `forward := true`, or `rel _ (head ..)`, if `forward := false`. +`arity` is the number of arguments in `..`. This is used by the `grw` tactic. -/ +def findGCongrLemmas?' (relName head : Name) (forward : Bool) (arity : Nat) : + CoreM (List GCongrLemma) := do + let lemmas := gcongrExt.getState (← getEnv) + let some lemmas := lemmas.get? { relName, head, arity } | return [] + if forward then + return lemmas.filter (·.keys.head!.head == head) + else + return lemmas.filter (·.keys.getLast!.head == head) + /-- 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 @@ -254,17 +276,23 @@ def updateRel (r e : Expr) (isLhs : Bool) : Expr := /-- Try to construct the `GCongrLemma` for a lemma with hypotheses `hyps` and conclusion `target`. -/ def makeGCongrLemma (hyps : Array Expr) (target : Expr) (declName : Name) (prio : Nat) - (forGrw : Bool) : MetaM GCongrLemma := do + (strict forGrw : Bool) : MetaM GCongrLemma := do let fail {α} (m : MessageData) : MetaM α := throwError "\ @[gcongr] attribute only applies to lemmas proving f x₁ ... xₙ ∼ f x₁' ... xₙ'.\n \ {m} in {target}" -- verify that conclusion of the lemma is of the form `f x₁ ... xₙ ∼ f x₁' ... xₙ'` let some (relName, lhs, rhs) := getRel (← whnf target) | fail "No relation found" let lhs := lhs.headBeta; let rhs := rhs.headBeta -- this is required for `Monotone fun x => ⋯` - 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 some (lhsHead, lhsArgs) := getCongrAppFnArgs lhs | fail "LHS is not suitable for congruence" + let some (rhsHead, rhsArgs) := getCongrAppFnArgs rhs | fail "RHS is not suitable for congruence" + if strict then + if lhsHead == rhsHead then + fail "LHS and RHS have the same head function." + else + if lhsHead != rhsHead then + fail "LHS and RHS do not have the same head function." + if lhsArgs.size != rhsArgs.size then + fail "LHS and RHS do not have the same number of arguments." let mut pairs := #[] -- iterate through each pair of corresponding (LHS/RHS) inputs to the head function `head` in -- the conclusion of the lemma @@ -274,7 +302,9 @@ def makeGCongrLemma (hyps : Array Expr) (target : Expr) (declName : Name) (prio if ← isDefEq lhs rhs <||> (isProof lhs <&&> isProof rhs) then continue let lhs := lhs.eta; let rhs := rhs.eta -- verify that the "varying argument" pairs are free variables (after eta-reduction) - unless lhs.isFVar && rhs.isFVar do fail "Not all varying arguments are free variables" + unless lhs.isFVar && rhs.isFVar do + if strict then continue -- When comparing `a ≤ b` with `a < b`, the instances are different. + fail "Not all varying arguments are free variables" -- Instance implicit arguments should be synthesized, rather than solved by congruence if (← lhs.fvarId!.getBinderInfo).isInstImplicit && (← rhs.fvarId!.getBinderInfo).isInstImplicit then @@ -320,110 +350,143 @@ def makeGCongrLemma (hyps : Array Expr) (target : Expr) (declName : Name) (prio This means that the `@[gcongr]` lemma cannot be used in the `grw` tactic. \ Please use `@[gcongr only]` instead." -- store all the information from this parse of the lemma's structure in a `GCongrLemma` - let key := { relName, head, arity := lhsArgs.size } - return { key, declName, mainSubgoals, numHyps := hyps.size, prio, numVarying, forGrw } + let mut keys := [{ relName, head := rhsHead, arity := lhsArgs.size }] + if strict then + keys := { relName, head := lhsHead, arity := lhsArgs.size } :: keys + return { keys, declName, mainSubgoals, numHyps := hyps.size, prio, numVarying, forGrw } /-- 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. +`z₁`/`z₂`) are all free variables. These lemmas are used by the `gcongr` and `grw` tactics. 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₂`, where the arguments of `f₁` and `f₂` are the same list of variables which have to be bound by the preceding `∀`. (Other antecedents are considered to generate "side goals".) -Use `gcongr only` to relax these conditions. A `gcongr only` lemma is not used by `grw`. If a lemma such as `add_le_add : a ≤ b → c ≤ d → a + c ≤ b + d` has been tagged with `gcongr`, then a direct consequence like `a ≤ b → a + c ≤ b + c` does *not* need to be tagged. However, if a more specific lemma has fewer side conditions, it should also be tagged with `gcongr`. For example, `mul_le_mul_of_nonneg_right` and `mul_le_mul_of_nonneg_left` are both tagged. -Lemmas involving `<` or `≤` can also be marked `@[bound]` for use in the related `bound` tactic. -/ -syntax (name := gcongrAttr) "gcongr" (&" only")? (prio)? : attr +* `gcongr only` relaxes some checks that ensure that the lemma is suitable for use in `grw`. + So, a lemma tagged `gcongr only` is not used by `grw`, but it may still be used by `gcongr`. +* `gcongr strict` lets you tag lemmas where the conclusion relates two different constants, + instead of a constant with itself. + This allows `grw` to replace one constant with another while doing a rewrite. + In particular, we use this to have special support for rewriting with strict inequalities (`<`). + This is done by applying `gcongr strict` to `lt_of_lt_of_le`, which has the conclusion + `b ≤ c → a < c` that relates `LE.le` with `LT.lt` (and similarly for `lt_of_lt_of_le'`). + As a result, a rewrite with a strict inequality can turn `<` into `≤`, or `≤` into `<`, + depending on whether this appears in positive or negative position. +-/ +syntax (name := gcongrAttr) "gcongr" (&" strict")? (&" only")? (prio)? : attr + +/-- Mark `declName` with the `gcongr` attribute. -/ +def addGCongrLemma (declName : Name) (stx : Syntax) (kind : AttributeKind) : MetaM Unit := + withReducible do + let strict := !stx[1].isNone + let forGrw := stx[2].isNone + let prio ← getAttrParamOptPrio stx[3] + let cinfo ← getConstInfo declName + let type := cinfo.type + forallTelescope type fun xs type => do + -- Special case the unfolding of `Monotone`-like conclusions. + if type.getAppFn.constName? matches + `Monotone | `Antitone | `StrictMono | `StrictAnti | + `MonotoneOn | `AntitoneOn | `StrictMonoOn | `StrictAntiOn then + forallTelescope (← withDefault <| unfoldDefinition type) fun xs' type => do + gcongrExt.add (← makeGCongrLemma (xs ++ xs') type declName prio strict forGrw) kind + return + -- If the conclusion is a free variable, it is a lemma like `imp_imp_imp` or `forall_imp`, + -- so we revert the last two variables. + if type.getAppFn.isFVar then + let type ← mkForallFVars xs[(xs.size-2)...xs.size] type + gcongrExt.add (← makeGCongrLemma xs.pop.pop type declName prio strict forGrw) kind + return + try + -- Add a `gcongr` lemma in the "normal" way. + gcongrExt.add (← makeGCongrLemma xs type declName prio strict forGrw) kind + catch e => try + match_expr type with + | Iff lhs rhs => addIffGCongrLemma prio strict forGrw cinfo xs lhs rhs + | _ => addImpGCongrLemma prio strict forGrw cinfo xs type + catch _ => + -- If none of the methods work, we throw the error thrown by the "normal" attempt. + throw e +where + /-- Assuming the lemma has conslusion `lhs ↔ rhs`, mark one of the implications + `lhs → rhs` or `rhs → lhs` as a `gcongr` lemma. -/ + addIffGCongrLemma (prio : Nat) (strict forGrw : Bool) (cinfo : ConstantInfo) + (xs : Array Expr) (lhs rhs : Expr) : MetaM Unit := do + try + -- Try using the `←` implication. + withLocalDeclD `_a rhs fun x => do + let gcongrLemma ← makeGCongrLemma (xs.push x) lhs declName prio strict forGrw + let auxType ← mkForallFVars (xs.push x) lhs + let auxValue ← mkLambdaFVars xs <| mkApp3 (.const ``Iff.mpr []) lhs rhs <| + mkAppN (.const declName (cinfo.levelParams.map .param)) xs + let auxDeclName ← mkAuxLemma cinfo.levelParams auxType auxValue (kind? := `_gcongr) + (forceExpose := true) + gcongrExt.add { gcongrLemma with declName := auxDeclName } kind + catch _ => + -- Try using the `→` implication. + withLocalDeclD `_a lhs fun x => do + let gcongrLemma ← makeGCongrLemma (xs.push x) rhs declName prio strict forGrw + let auxType ← mkForallFVars (xs.push x) rhs + let auxValue ← mkLambdaFVars xs <| mkApp3 (.const ``Iff.mp []) lhs rhs <| + mkAppN (.const declName (cinfo.levelParams.map .param)) xs + let auxDeclName ← mkAuxLemma cinfo.levelParams auxType auxValue (kind? := `_gcongr) + (forceExpose := true) + gcongrExt.add { gcongrLemma with declName := auxDeclName } kind + /-- + Mark the lemma as a `gcongr` lemma, using implication as the relation. + For example, `Or.imp : (a → c) → (b → d) → a ∨ b → c ∨ d`. In the non-strict case, + we want to support such lemmas even if the hypotheses are given in a different order. + So, we find the last hypothesis whose head matches that of the conclusion, + and move it to the end. + -/ + addImpGCongrLemma (prio : Nat) (strict forGrw : Bool) (cinfo : ConstantInfo) + (xs : Array Expr) (type : Expr) : MetaM Unit := do + if strict then + let some x := xs.back? | failure + let type ← mkForallFVars #[x] type + let gcongrLemma ← makeGCongrLemma xs.pop type declName prio strict forGrw + gcongrExt.add gcongrLemma kind + else + let .const c _ := type.getAppFn | failure + let rec findIdx (i : Nat) (h : i ≤ xs.size) : MetaM (Fin xs.size) := + match i with + | 0 => failure + | i + 1 => do + if (← inferType xs[i]).getAppFn.isConstOf c then + return ⟨i, by lia⟩ + else + findIdx i (by lia) + let i ← findIdx xs.size xs.size.le_refl + let type ← mkForallFVars #[xs[i]] type + let xs' := xs.eraseIdx i i.isLt + let gcongrLemma ← makeGCongrLemma xs' type declName prio strict forGrw + if i == xs.size - 1 then + -- The argument order is already correct. + gcongrExt.add gcongrLemma kind + else + -- We need to make an auxiliary theorem with the correct argument order. + let auxType ← mkForallFVars xs' type + let auxValue ← mkLambdaFVars (xs'.push xs[i]) <| + mkAppN (.const declName (cinfo.levelParams.map .param)) xs + let auxDeclName ← mkAuxLemma cinfo.levelParams auxType auxValue (kind? := `_gcongr) + (forceExpose := true) + gcongrExt.add { gcongrLemma with declName := auxDeclName } kind @[inherit_doc gcongrAttr] initialize registerBuiltinAttribute { name := `gcongrAttr descr := "generalized congruence" - add := fun declName stx kind ↦ MetaM.run' do withReducible do - let forGrw := stx[1].isNone - let prio ← getAttrParamOptPrio stx[2] - let cinfo ← getConstInfo declName - let type := cinfo.type - forallTelescope type fun xs type => do - -- Special case the unfolding of `Monotone`-like conclusions. - if type.getAppFn.constName? matches - `Monotone | `Antitone | `StrictMono | `StrictAnti | - `MonotoneOn | `AntitoneOn | `StrictMonoOn | `StrictAntiOn then - forallTelescope (← withDefault <| unfoldDefinition type) fun xs' type => do - gcongrExt.add (← makeGCongrLemma (xs ++ xs') type declName prio forGrw) kind - return - -- If the conclusion is a free variable, it is a lemma like `imp_imp_imp` or `forall_imp`, - -- so we revert the last two variables. - if type.getAppFn.isFVar then - let type ← mkForallFVars xs[(xs.size-2)...xs.size] type - gcongrExt.add (← makeGCongrLemma xs.pop.pop type declName prio forGrw) kind - return - try - -- Add a `gcongr` lemma in the "normal" way. - gcongrExt.add (← makeGCongrLemma xs type declName prio forGrw) kind - catch e => try - match_expr type with - | Iff lhs rhs => - -- When the goal is an `↔`, try to use either of the implications. - try - -- Try using the `←` implication. - withLocalDeclD `_a rhs fun x => do - let gcongrLemma ← makeGCongrLemma (xs.push x) lhs declName prio forGrw - let auxType ← mkForallFVars (xs.push x) lhs - let auxValue ← mkLambdaFVars xs <| mkApp3 (.const ``Iff.mpr []) lhs rhs <| - mkAppN (.const declName (cinfo.levelParams.map .param)) xs - let auxDeclName ← mkAuxLemma cinfo.levelParams auxType auxValue (kind? := `_gcongr) - gcongrExt.add { gcongrLemma with declName := auxDeclName } kind - catch _ => - -- Try using the `→` implication. - withLocalDeclD `_a lhs fun x => do - let gcongrLemma ← makeGCongrLemma (xs.push x) rhs declName prio forGrw - let auxType ← mkForallFVars (xs.push x) rhs - let auxValue ← mkLambdaFVars xs <| mkApp3 (.const ``Iff.mp []) lhs rhs <| - mkAppN (.const declName (cinfo.levelParams.map .param)) xs - let auxDeclName ← mkAuxLemma cinfo.levelParams auxType auxValue (kind? := `_gcongr) - gcongrExt.add { gcongrLemma with declName := auxDeclName } kind - | _ => - -- Try to interpret the lemma as an implicational `gcongr` lemma, - -- such as `Or.imp : (a → c) → (b → d) → a ∨ b → c ∨ d`. - -- We want to support such lemmas even if the hypotheses are given in a different order. - -- So, we find the last hypothesis whose head matches that of the conclusion, - -- and move it to the end. - let .const c _ := type.getAppFn | failure - let rec findIdx (i : Nat) (h : i ≤ xs.size) : MetaM (Fin xs.size) := - match i with - | 0 => failure - | i + 1 => do - if (← inferType xs[i]).getAppFn.isConstOf c then - return ⟨i, by lia⟩ - else - findIdx i (by lia) - let i ← findIdx xs.size xs.size.le_refl - let type ← mkForallFVars #[xs[i]] type - let xs' := xs.eraseIdx i i.isLt - let gcongrLemma ← makeGCongrLemma xs' type declName prio forGrw - if i == xs.size - 1 then - -- The argument order is already correct. - gcongrExt.add gcongrLemma kind - else - -- We need to make an auxiliary theorem with the correct argument order. - let auxType ← mkForallFVars xs' type - let auxValue ← mkLambdaFVars (xs'.push xs[i]) <| - mkAppN (.const declName (cinfo.levelParams.map .param)) xs - let auxDeclName ← mkAuxLemma cinfo.levelParams auxType auxValue (kind? := `_gcongr) - gcongrExt.add { gcongrLemma with declName := auxDeclName } kind - catch _ => - -- If none of the methods work, we throw the error thrown by the "normal" attempt. - throw e + add := (addGCongrLemma · · · |>.run') } initialize registerTraceClass `Meta.gcongr @@ -554,7 +617,7 @@ def relImpRelLemma (arity : Nat) : List GCongrLemma := declName := ``rel_imp_rel mainSubgoals := #[⟨5, 3, 7, [], true⟩, ⟨4, 6, 8, [], false⟩] numHyps := 9 - key := default, prio := default, numVarying := default, forGrw := true + keys := default, prio := default, numVarying := default, forGrw := true }] end Trans @@ -709,14 +772,13 @@ partial def _root_.Lean.MVarId.gcongr let some (rhsHead, rhsArgs) := getCongrAppFnArgs rhs | if mdataLhs?.isNone then pushNewGoal g; return false throwTacticEx `gcongr g m!"the head of {rhs} is not a constant" - unless lhsHead == rhsHead && lhsArgs.size == rhsArgs.size do + unless lhsArgs.size == rhsArgs.size do if mdataLhs?.isNone then pushNewGoal g; return false - throwTacticEx `gcongr g m!"{lhs} and {rhs} are not of the same shape" + throwTacticEx `gcongr g m!"{lhs} and {rhs} have a different number of arguments" let mctx ← getMCtx -- Look up the `@[gcongr]` lemmas whose conclusion has the same relation and head function as -- the goal - let key := { relName, head := lhsHead, arity := lhsArgs.size } - let mut lemmas := (gcongrExt.getState (← getEnv)).getD key [] + let mut lemmas ← findGCongrLemmas? relName lhsHead rhsHead lhsArgs.size if relName == `_Implies then lemmas := lemmas ++ relImpRelLemma lhsArgs.size for lem in lemmas do diff --git a/Mathlib/Tactic/GRewrite/Core.lean b/Mathlib/Tactic/GRewrite/Core.lean index 298658490b1842..1b5311dcc33df0 100644 --- a/Mathlib/Tactic/GRewrite/Core.lean +++ b/Mathlib/Tactic/GRewrite/Core.lean @@ -16,10 +16,10 @@ This module defines the core of the `grw`/`grewrite` tactic. This file provides two implementations of the tactic: 1. The simple implementation uses `kabstract` to determine where to rewrite, and then calls `MVarId.gcongr` to prove that the rewrite is valid. - This is used by `nth_grw` and `grw'` + This is used by `nth_grw` and `grw +useKAbstract`. 2. The more sophisticated implementation has its own congruence loop, applying `gcongr` lemmas to - create the replacement expression, while at the same time proving that this is related to the - original expression. + create the replacement expression, and to prove that this is related to the original expression. + This supports the use of strict inequalities to change the strictness in the goal. This is used by `grw` and `apply_rw`. -/ @@ -313,8 +313,7 @@ partial def grewriteCore (relName : Name) (rel? : Option Expr) (e : Expr) (forwa return (mvar, goal) -- Try all applicable `@[gcongr]` lemmas. if let some (head, args) := getCongrAppFnArgs e then - let key := { relName, head, arity := args.size } - let mut lemmas := (gcongrExt.getState (← getEnv)).getD key [] + let mut lemmas ← findGCongrLemmas?' relName head forward args.size if relName == `_Implies then lemmas := lemmas ++ relImpRelLemma args.size let mctx ← getMCtx diff --git a/Mathlib/Tactic/GRewrite/Elab.lean b/Mathlib/Tactic/GRewrite/Elab.lean index 97a31d8929e9f2..213dfd28dc98df 100644 --- a/Mathlib/Tactic/GRewrite/Elab.lean +++ b/Mathlib/Tactic/GRewrite/Elab.lean @@ -120,8 +120,7 @@ declare_config_elab elabGRewriteConfig GRewrite.Config /-- `grewrite [e₁, ..., eₙ]` uses each expression `eᵢ : Rᵢ aᵢ bᵢ` (where `Rᵢ` is any two-argument relation) as a generalized rewrite rule on the main goal, replacing occurrences of `aᵢ` with `bᵢ`. -Occurrences of `bᵢ` are not rewritten, even if logically possible. Use `grewrite [← eᵢ]` to rewrite -in the other direction, replacing occurrences of `bᵢ` with `aᵢ`. +Use `grewrite [← eᵢ]` to rewrite in the other direction, replacing occurrences of `bᵢ` with `aᵢ`. If an expression `e` is a defined constant, then the equational theorems associated with `e` are used. This provides a convenient way to unfold `e`. If `e` has parameters, the tactic will try to @@ -131,13 +130,12 @@ after unification will create side goals. 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. -The strict inequality `a < b` is turned into the non-strict inequality `a ≤ b` to rewrite with it. -A future version of `grewrite` may get special support for making better use of strict inequalities. + +Rewriting with a strict inequality `a < b` may change a constant in the goal, +such as changing `<` to `≤`. If this is not possible, then `a < b` is treated the same as `a ≤ b`. `grw` is like `grewrite` but tries to close the goal afterwards by "cheap" (reducible) `rfl`. To rewrite only in the `n`-th position, use `nth_grewrite n`. -This is useful when `grewrite` tries to rewrite in a position that is not valid for the given -relation. `apply_rewrite [e₁, ..., eₙ]` is a shorthand for `grewrite +implicationHyp [e₁, ..., eₙ]`: it interprets `· → ·` as a relation instead of adding the hypothesis as a side condition. @@ -166,8 +164,7 @@ public def evalGRewriteSeq : Tactic := fun stx => do `grw [e₁, ..., eₙ]` uses each expression `eᵢ : Rᵢ aᵢ bᵢ` (where `Rᵢ` is any two-argument relation) as a generalized rewrite rule on the main goal, replacing occurrences of `aᵢ` with `bᵢ`, then tries to close the main goal by "cheap" (reducible) `rfl`. -Occurrences of `bᵢ` are not rewritten, even if logically possible. Use `grw [← eᵢ]` to rewrite -in the other direction, replacing occurrences of `bᵢ` with `aᵢ`. +Use `grw [← eᵢ]` to rewrite in the other direction, replacing occurrences of `bᵢ` with `aᵢ`. If an expression `e` is a defined constant, then the equational theorems associated with `e` are used. This provides a convenient way to unfold `e`. If `e` has parameters, the tactic will try to @@ -177,14 +174,14 @@ after unification will create side goals. 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. -The strict inequality `a < b` is turned into the non-strict inequality `a ≤ b` to rewrite with it. -A future version of `grw` may get special support for making better use of strict inequalities. + +Rewriting with a strict inequality `a < b` may change the goal, such as changing `<` to `≤`. +If this is not possible, then `a < b` is treated the same as `a ≤ b`. `grewrite` is like `grw` but does not try to apply `rfl` afterwards. To rewrite only in the `n`-th position, use `nth_grw n`. -This is useful when `grw` tries to rewrite in a position that is not valid for the given relation. -`apply_rw [rules]` is a shorthand for `grw +implicationHyp [rules]`: it interprets `· → ·` as a -relation instead of adding the hypothesis as a side condition. +`apply_rw [e₁, ..., eₙ]` is a shorthand for `grw +implicationHyp [e₁, ..., eₙ]`: it interprets +`· → ·` as a relation instead of adding the hypothesis as a side condition. * `grw [← e]` applies the rewrite rule `e : R a b` in the reverse direction, replacing occurrences of `b` with `a`. @@ -192,7 +189,7 @@ relation instead of adding the hypothesis as a side condition. details. * To let `grw` unfold more aggressively, as in `erw`, use `grw (transparency := default) [e₁, ..., eₙ]`. - * `grw +implicationHyp [e₁, ..., e\_n]` interprets `· → ·` as a relation (see `apply_rw`). + * `grw +implicationHyp [e₁, ..., eₙ]` interprets `· → ·` as a relation (see `apply_rw`). * `grw [e₁, ..., eₙ] at l` rewrites at the location(s) `l`. Examples: @@ -263,8 +260,7 @@ macro (name := applyRwSeq) "apply_rw " c:optConfig s:rwRuleSeq loc:(location)? : /-- `nth_grewrite n₁ ... nₖ [e₁, ..., eₙ]` is a variant of `grewrite` that for each expression `eᵢ : R aᵢ bᵢ` only replaces the `n₁, ..., nₖ`th occurrence of `aᵢ` with `bᵢ`. -Occurrences of `bᵢ` are not rewritten, even if logically possible. Use -`nth_grewrite n₁ ... nₖ [← eᵢ]` to rewrite in the other direction, replacing occurrences of `bᵢ` +Use `nth_grewrite n₁ ... nₖ [← eᵢ]` to rewrite in the other direction, replacing occurrences of `bᵢ` with `aᵢ`. If an expression `e` is a defined constant, then the equational theorems associated with `e` are @@ -275,9 +271,8 @@ after unification will create side goals. To be able to use `nth_grewrite`, the relevant lemmas need to be tagged with `@[gcongr]`. To rewrite inside a transitive relation, you can also give it an `IsTrans` instance. -The strict inequality `a < b` is turned into the non-strict inequality `a ≤ b` to rewrite with it. -A future version of `nth_grewrite` may get special support for making better use of strict -inequalities. +Rewriting with a strict inequality `a < b` may change the strictness of the goal, +replacing a goal `_ < _` by `_ ≤ _`. If this is not possible, then `a < b` is treated as `a ≤ b`. * `nth_grewrite n₁ ... nₖ [← e]` applies the rewrite rule `e : R a b` in the reverse direction, replacing the `n₁, ..., nₖ`th occurrences of `b` with `a`. @@ -294,9 +289,8 @@ macro "nth_grewrite" c:optConfig ppSpace nums:(num)+ s:rwRuleSeq loc:(location)? /-- `nth_grw n₁ ... nₖ [e₁, ..., eₙ]` is a variant of `grw` that for each expression `eᵢ : R aᵢ bᵢ` only -replaces the `n₁, ..., nₖ`th occurrence of `aᵢ` with `bᵢ`. Occurrences of `bᵢ` are not rewritten, -even if logically possible. Use `nth_grw n₁ ... nₖ [← eᵢ]` to rewrite in the other direction, -replacing occurrences of `bᵢ` with `aᵢ`. +replaces the `n₁, ..., nₖ`th occurrence of `aᵢ` with `bᵢ`. Use `nth_grw n₁ ... nₖ [← eᵢ]` to rewrite +in the other direction, replacing occurrences of `bᵢ` with `aᵢ`. If an expression `e` is a defined constant, then the equational theorems associated with `e` are used. This provides a convenient way to unfold `e`. If `e` has parameters, the tactic will try to @@ -306,9 +300,8 @@ after unification will create side goals. To be able to use `nth_grw`, the relevant lemmas need to be tagged with `@[gcongr]`. To rewrite inside a transitive relation, you can also give it an `IsTrans` instance. -The strict inequality `a < b` is turned into the non-strict inequality `a ≤ b` to rewrite with it. -A future version of `nth_grw` may get special support for making better use of strict -inequalities. +Rewriting with a strict inequality `a < b` may change the strictness of the goal, +replacing a goal `_ < _` by `_ ≤ _`. If this is not possible, then `a < b` is treated as `a ≤ b`. * `nth_grw n₁ ... nₖ [← e]` applies the rewrite rule `e : R a b` in the reverse direction, replacing the `n₁, ..., nₖ`th occurrences of `b` with `a`. diff --git a/Mathlib/Tactic/GrindAttrs.lean b/Mathlib/Tactic/GrindAttrs.lean new file mode 100644 index 00000000000000..f36fea44632151 --- /dev/null +++ b/Mathlib/Tactic/GrindAttrs.lean @@ -0,0 +1,110 @@ +/- +Copyright (c) 2026 Floris van Doorn. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Floris van Doorn +-/ + +module + +public import Lean.Meta.Tactic.Grind.RegisterCommand +public import Mathlib.Init + +/-! +# Custom grind-sets + +In this file we declare custom grind attributes and tactics that call grind using only these grind +attributes. These grind sets are helpful because they can contain a lot of specialized ways to +prove a particular problem. + +Currently, this implements the `compactness` and `closedness` grind attribute and tactic. + +## Usage Notes + +These tactics can be useful for various purposes: +* directly as a tactic: `by compactness` +* as auto-params for lemmas: `(h : IsCompact K := by compactness)` +* as discharger for other tactics, e.g. `fun_prop (disch := compactness)` +* You can also use the grind sets directly: `grind only [compactness, closedness]`. + This is especially useful if you want to combine multiple grind sets. + +## Implementation Notes + +We define these grind sets so that we can aggressively tag lemmas in one particular topic as +grind lemmas for a particular grind set. For the default grind set we should be a lot +more careful with tagging lemmas, to avoid slowing down `grind`, but since these specialized grind +attributes don't have any tagged lemmas outside its specialized domain, it should still be +performant. + +These tactics will not use the full power of grind, and could disable some of the grind engines if +these would slow down these tactics. We could have alternatively used a tactic similar to +`apply_rules` here, but we think that the efficient implementation of `grind` is helpful even for +these simpler tactics. For example, we can safely tag both the following lemmas, and `grind` will +add both pairs of hypotheses to the whiteboard without having to backtrack. +``` +IsCompact.inter_left : IsClosed s → IsCompact t → IsCompact (s ∩ t) +IsCompact.inter_right : IsCompact s → IsClosed t → IsCompact (s ∩ t) +``` + +We will tag transition theorems, e.g. `Set.Finite.isCompact : Finite s → IsCompact s` should be +tagged `@[compactness .]`, even if `compactness` won't contain lemmas about finite sets. +The advantages of this are that we can use local finiteness hypotheses, and this will ensure that +the different grind sets will interact well with each other. For the same reason we tag lemmas +that involve other properties, e.g. `IsCompact.inter_left` + +## To do + +* Implement other grind sets, e.g. `boundedness`, `countability`, `connectedness`, ... + +-/ + +open Lean Parser Tactic + +/-- A hash set of the grind attributes in Mathlib. + +When adding a new grind attribute, manually add it to this hash set as well. -/ +def Mathlib.grindAttrs : Std.HashSet Name := + {`compactness, `closedness} + +/-- The `compactness` attribute is a custom grind-set specialized to prove that sets are compact. +It is called by the `compactness` tactic. -/ +register_grind_attr compactness + +/-- +`compactness` is a simple tactic that tries various lemmas to prove that a set is compact. +It is implemented using `grind`, and has the same configuration options as `grind`. + +Use `grind only [compactness, closedness]` instead if you want to prove that the closure of sets are +compact. + +It also exists as a grind attribute, and can be combined with other grind attributes using +`grind only [compactness, ...]`. +-/ +macro (name := compactnessTac) "compactness" config:optConfig : tactic => + -- note: directly giving `compactness` as argument in the syntax quotation below is treated + -- as an unknown identifier by the hygiene system. + `(tactic|grind $config only [$(mkIdent `compactness):term]) + +@[inherit_doc compactnessTac] +macro "compactness?" config:optConfig : tactic => + `(tactic|grind? $config only [$(mkIdent `compactness):term]) + +/-- The `closedness` attribute is a custom grind-set specialized to prove that sets are closed. +It is called by the `closedness` tactic. -/ +register_grind_attr closedness + +/-- +`closedness` is a simple tactic that tries various lemmas to prove that a set is closed, +and reasoning about the closure of sets. +It is implemented using `grind`, and has the same configuration options as `grind`. + +It also exists as a grind attribute, and can be combined with other grind attributes using +`grind only [closedness, ...]`. +-/ +macro (name := closednessTac) "closedness" config:optConfig : tactic => + -- note: directly giving `closedness` as argument in the syntax quotation below is treated + -- as an unknown identifier by the hygiene system. + `(tactic|grind $config only [$(mkIdent `closedness):term]) + +@[inherit_doc closednessTac] +macro "closedness?" config:optConfig : tactic => + `(tactic|grind? $config only [$(mkIdent `closedness):term]) diff --git a/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean b/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean index d89a10b0fce4e1..190798ae8eab1c 100644 --- a/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean +++ b/Mathlib/Tactic/Linarith/Oracle/SimplexAlgorithm/Gauss.lean @@ -65,13 +65,13 @@ def getTableauImp : GaussM n m matType <| Tableau matType := do for i in [col:m] do free := free.push i - let ansMatrix : matType basic.size free.size := ← do + let ansMatrix : matType basic.size free.size ← do let vals := getValues (← get) |>.filterMap fun (i, j, v) => if j == basic[i]! then none else some (i, free.findIdx? (· == j) |>.get!, -v) - return ofValues vals + pure <| ofValues vals return ⟨basic, free, ansMatrix⟩ diff --git a/Mathlib/Tactic/Linarith/Parsing.lean b/Mathlib/Tactic/Linarith/Parsing.lean index 4aa27c1689a328..bca51df08a21ba 100644 --- a/Mathlib/Tactic/Linarith/Parsing.lean +++ b/Mathlib/Tactic/Linarith/Parsing.lean @@ -62,13 +62,10 @@ local instance {α β : Type*} {c : α → α → Ordering} [Add β] [Zero β] [ namespace Mathlib.Tactic.Linarith -/-- A local abbreviation for `TreeMap` so we don't need to write `Ord.compare` each time. -/ -abbrev Map (α β) [Ord α] := TreeMap α β Ord.compare - /-! ### Parsing datatypes -/ /-- Variables (represented by natural numbers) map to their power. -/ -abbrev Monom : Type := Map ℕ ℕ +abbrev Monom : Type := TreeMap ℕ ℕ /-- `1` is represented by the empty monomial, the product of no variables. -/ def Monom.one : Monom := TreeMap.empty @@ -83,7 +80,7 @@ instance : Ord Monom where compare x y := if x.lt y then .lt else if x == y then .eq else .gt /-- Linear combinations of monomials are represented by mapping monomials to coefficients. -/ -abbrev Sum : Type := Map Monom ℤ +abbrev Sum : Type := TreeMap Monom ℤ /-- `1` is represented as the singleton sum of the monomial `Monom.one` with coefficient 1. -/ def Sum.one : Sum := TreeMap.empty.insert Monom.one 1 @@ -200,7 +197,7 @@ The output `TreeMap ℕ ℤ` has the same structure as `s : Sum`, but each monomial key is replaced with its index according to `map`. If any new monomials are encountered, they are assigned variable numbers and `map` is updated. -/ -def elimMonom (s : Sum) (m : Map Monom ℕ) : Map Monom ℕ × Map ℕ ℤ := +def elimMonom (s : Sum) (m : TreeMap Monom ℕ) : TreeMap Monom ℕ × TreeMap ℕ ℤ := s.foldr (fun mn coeff ⟨map, out⟩ ↦ match map[mn]? with | some n => ⟨map, out.insert n coeff⟩ @@ -216,8 +213,8 @@ into a `comp` object. `e_map` maps atomic expressions to indices; `monom_map` maps monomials to indices. Both of these are updated during processing and returned. -/ -def toComp (red : TransparencyMode) (e : Expr) (e_map : ExprMap) (monom_map : Map Monom ℕ) : - MetaM (Comp × ExprMap × Map Monom ℕ) := do +def toComp (red : TransparencyMode) (e : Expr) (e_map : ExprMap) (monom_map : TreeMap Monom ℕ) : + MetaM (Comp × ExprMap × TreeMap Monom ℕ) := do let (iq, e) ← parseCompAndExpr e let (m', comp') ← linearFormOfExpr red e_map e let ⟨nm, mm'⟩ := elimMonom comp' monom_map @@ -228,8 +225,8 @@ def toComp (red : TransparencyMode) (e : Expr) (e_map : ExprMap) (monom_map : Ma `toCompFold red e_map exprs monom_map` folds `toComp` over `exprs`, updating `e_map` and `monom_map` as it goes. -/ -def toCompFold (red : TransparencyMode) : ExprMap → List Expr → Map Monom ℕ → - MetaM (List Comp × ExprMap × Map Monom ℕ) +def toCompFold (red : TransparencyMode) : ExprMap → List Expr → TreeMap Monom ℕ → + MetaM (List Comp × ExprMap × TreeMap Monom ℕ) | m, [], mm => return ([], m, mm) | m, (h::t), mm => do let (c, m', mm') ← toComp red h m mm diff --git a/Mathlib/Tactic/LinearCombinationPrime.lean b/Mathlib/Tactic/LinearCombinationPrime.lean index 6b71f8dab9737e..530e066e78ee2e 100644 --- a/Mathlib/Tactic/LinearCombinationPrime.lean +++ b/Mathlib/Tactic/LinearCombinationPrime.lean @@ -94,7 +94,7 @@ partial def expandLinearCombo (ty : Expr) (stx : Syntax.Term) : TermElabM Expand match ← expandLinearCombo ty e with | .const c => .const <$> `(-$c) | .proof p => .proof <$> ``(neg_pf $p) - | `(← $e) => do + | `(← $e:term) => do match ← expandLinearCombo ty e with | .const c => return .const c | .proof p => .proof <$> ``(Eq.symm $p) diff --git a/Mathlib/Tactic/Linter/DirectoryDependency.lean b/Mathlib/Tactic/Linter/DirectoryDependency.lean index 4fae86eecdf634..9e9bd3ca160442 100644 --- a/Mathlib/Tactic/Linter/DirectoryDependency.lean +++ b/Mathlib/Tactic/Linter/DirectoryDependency.lean @@ -617,6 +617,7 @@ def overrideAllowedImportDirs : NamePrefixRel := .ofArray #[ (`Mathlib.Analysis.Convex.SimplicialComplex.AffineIndependentUnion, `Mathlib.AlgebraicTopology), (`Mathlib.Probability.Kernel.Category, `Mathlib.CategoryTheory), -- For the category of s-finite/Markov kernels (`Mathlib.RepresentationTheory.Continuous, `Mathlib.Topology), -- For continuous representations + (`Mathlib.RepresentationTheory.Homological.ContCohomology, `Mathlib.Topology), -- For continuous cohomology -- TODO: think about the role of Analysis and Algebra, and perhaps further separation (`Mathlib.Algebra.Order.Archimedean.Real, `Mathlib.Analysis), (`Mathlib.Algebra.Star.CHSH, `Mathlib.Analysis), diff --git a/Mathlib/Tactic/Linter/DocString.lean b/Mathlib/Tactic/Linter/DocString.lean index 8b3182d24d9ca4..2fcbff3dfc4ddb 100644 --- a/Mathlib/Tactic/Linter/DocString.lean +++ b/Mathlib/Tactic/Linter/DocString.lean @@ -149,6 +149,8 @@ def docStringLinter : Linter where run := withSetOptionIn fun stx ↦ do let currIndent := fm.toPosition pos |>.column if docStx.isMissing then continue -- this is probably superfluous, thanks to `some pos` above. + -- ignore antiquotations from syntax patterns like `$(_)?` + unless docStx.getKind == ``Parser.Command.docComment do continue -- `docString` contains e.g. trailing spaces before the `-/`, but does not contain -- any leading whitespace before the actual string starts. let docString ← try getDocStringText ⟨docStx⟩ catch _ => continue diff --git a/Mathlib/Tactic/Linter/FlexibleLinter.lean b/Mathlib/Tactic/Linter/FlexibleLinter.lean index b7de42cc5db889..87635d731ab532 100644 --- a/Mathlib/Tactic/Linter/FlexibleLinter.lean +++ b/Mathlib/Tactic/Linter/FlexibleLinter.lean @@ -522,8 +522,12 @@ def flexibleLinter : Linter where run := withSetOptionIn fun _stx => do let suggestion? ← liftCoreM <| generateSimpSuggestion stainData stainStx -- Emit warning and suggestion let msg := match stainStx.getKind with - | ``Lean.Parser.Tactic.simp => - m!"`{stainStr}` is a flexible tactic modifying `{d}`. \ + | ``Lean.Parser.Tactic.simp => match d with + | .wildcard => m!"`{stainStr}` is a flexible tactic that potentially modifies all \ + hypotheses and the current goal with a wildcard `*`. \ + Try `simp?` and use the suggested `simp only [...]`. \ + Alternatively, use `suffices` to explicitly state the simplified form." + | _ => m!"`{stainStr}` is a flexible tactic modifying `{d}`. \ Try `simp?` and use the suggested `simp only [...]`. \ Alternatively, use `suffices` to explicitly state the simplified form." | ``Lean.Parser.Tactic.simpAll => @@ -539,7 +543,18 @@ def flexibleLinter : Linter where run := withSetOptionIn fun _stx => do if let some suggStx := suggestion? then liftCoreM <| Lean.Meta.Tactic.TryThis.addSuggestion stainStx { suggestion := .tsyntax (kind := `tactic) ⟨suggStx⟩ } (origSpan? := stainStx) - logInfoAt s m!"`{s}` uses `{d}`!" + let fm ← getFileMap + let stainLine? := stainStx.getPos?.map (Position.line ∘ fm.toPosition) + let lineStr := if let some line := stainLine? then s!" on line {line}" else "" + let atomStr := match stainStx[0] with + | .atom _ val => "the flexible tactic " ++ m!"`{val}`" + | _ => "a flexible tactic" + logInfoAt s <| match d with + | .name _ => m!"`{.group s}`\nuses `{d}`, which was modified by {atomStr}{lineStr}!" + | .goal => + m!"`{.group s}`\nmodifies the current goal, which was modified by {atomStr}{lineStr}!" + | .wildcard => m!"`{.group s}`\nuses a rigid tactic. Previously, {atomStr}, which \ + potentially modified all hypotheses and the goal with a wildcard `*`, was used{lineStr}." initialize addLinter flexibleLinter diff --git a/Mathlib/Tactic/Linter/HaveLetLinter.lean b/Mathlib/Tactic/Linter/HaveLetLinter.lean index a0322f4601c61d..65a3dfcc4bffbe 100644 --- a/Mathlib/Tactic/Linter/HaveLetLinter.lean +++ b/Mathlib/Tactic/Linter/HaveLetLinter.lean @@ -88,7 +88,7 @@ def toFormat_propTypes (ctx : ContextInfo) (lc : LocalContext) (es : Array (Expr also a `Format`ted version of the corresponding Type. -/ public partial def nonPropHaves : InfoTree → CommandElabM (Array (Syntax × Format)) := - InfoTree.foldInfoM (init := #[]) fun ctx info args => return args ++ (← do + InfoTree.foldInfoM (init := #[]) fun ctx info args => return args ++ (← (do let .ofTacticInfo i := info | return #[] let stx := i.stx let .original .. := stx.getHeadInfo | return #[] @@ -109,7 +109,7 @@ def nonPropHaves : InfoTree → CommandElabM (Array (Syntax × Format)) := -- Now, we get the `MetaM` state up and running to find the types of each entry of `newDecls`. -- For each entry which is a `Type`, we print a warning on `have`. let fmts ← toFormat_propTypes ctx lc (newDecls.map (fun e ↦ (e.type, e.userName))).toArray - return fmts.map fun (fmt, na) ↦ (stx, f!"{na} : {fmt}")) + return fmts.map fun (fmt, na) ↦ (stx, f!"{na} : {fmt}"))) /-- The main implementation of the `have` vs `let` linter. -/ def haveLetLinter : Linter where run := withSetOptionIn fun _stx => do diff --git a/Mathlib/Tactic/Linter/Header.lean b/Mathlib/Tactic/Linter/Header.lean index 73ecfad1d38d45..40b79e7b281843 100644 --- a/Mathlib/Tactic/Linter/Header.lean +++ b/Mathlib/Tactic/Linter/Header.lean @@ -366,7 +366,8 @@ The set of files outside the `Mathlib` package to run the header style linter on because they are files that test the linter. -/ def headerTestFiles : NameSet := .ofList - [`MathlibTest.Header, `MathlibTest.HeaderFail, `MathlibTest.VersoHeader, `MathlibTest.DirectoryDependencyLinter.Test] + [`MathlibTest.Linter.Header.Basic, `MathlibTest.Linter.Header.Fail, `MathlibTest.Linter.Header.Verso, + `MathlibTest.DirectoryDependencyLinter.Test] @[inherit_doc Mathlib.Linter.linter.style.header] def headerLinter : Linter where run := withSetOptionIn fun stx ↦ do diff --git a/Mathlib/Tactic/Linter/Lint.lean b/Mathlib/Tactic/Linter/Lint.lean index 1b1bd995958ab8..b46f67dd8ccc4a 100644 --- a/Mathlib/Tactic/Linter/Lint.lean +++ b/Mathlib/Tactic/Linter/Lint.lean @@ -11,7 +11,6 @@ module public import Batteries.Tactic.Lint -- shake: keep public import Lean.Linter.Deprecated public import Mathlib.Tactic.DeclarationNames -public import Batteries.Tactic.Lint.Basic /-! # Linters for Mathlib @@ -63,21 +62,19 @@ namespace Mathlib.Linter /-! ### `dupNamespace` linter -The `dupNamespace` linter produces a warning when a declaration contains the same namespace -at least twice consecutively. +The `dupNamespace` linter produces a warning when a component of a declaration name is repeated +several times. The repetition does not have to be consecutive. Examples: `Nat.Nat.foo`, +`One.two.two`, `Nat.One.Nat`, `Nat.Prime.Nat.Prime.foo`, `Nat.Prime.Nat.bar` -For instance, `Nat.Nat.foo` and `One.two.two` trigger a warning, while `Nat.One.Nat` does not. +The linter also warns about auto-generated declarations (such as, those generated by `to_additive`). -/ /-- -The `dupNamespace` linter is set on by default. Lean emits a warning on any declaration that -contains the same namespace at least twice consecutively. +The `dupNamespace` linter produces a warning when a component of a declaration name is repeated +several times. The repetition does not have to be consecutive. Examples: `Nat.Nat.foo`, +`One.two.two`, `Nat.One.Nat`, `Nat.Prime.Nat.Prime.foo`, `Nat.Prime.Nat.bar` -For instance, `Nat.Nat.foo` and `One.two.two` trigger a warning, while `Nat.One.Nat` does not. - -*Note.* -This linter will not detect duplication in namespaces of autogenerated declarations -(other than the one whose `declId` is present in the source declaration). +The linter also warns about auto-generated declarations (such as, those generated by `to_additive`). -/ public register_option linter.dupNamespace : Bool := { defValue := true @@ -96,12 +93,23 @@ def dupNamespace : Linter where run := withSetOptionIn fun stx ↦ do aliases ← getAliasSyntax exp for id in (← getNamesFrom (stx.getPos?.getD default)) ++ aliases do let declName := id.getId + -- We intentionally *do* lint deprecated declarations: + -- this is important since people can forget to add a `_root_` when adding deprecations. if declName.hasMacroScopes || isPrivateName declName then continue let nm := declName.components - let some (dup, _) := nm.zip (nm.tailD []) |>.find? fun (x, y) ↦ x == y - | continue - Linter.logLint linter.dupNamespace id - m!"The namespace '{dup}' is duplicated in the declaration '{declName}'" + -- Collect distinct components which appear more than once. + let duplicated := List.eraseDups <| nm.filter (fun comp ↦ nm.count comp > 1) + match duplicated with + | [] => continue + | [ns] => + Linter.logLint linter.dupNamespace id + m!"The namespace `{ns}` is duplicated in the declaration \ + `{.ofConstName (fullNames := true) declName}`." + | dup => + let ns := MessageData.andList (duplicated.map (m!"`{·}`")) + Linter.logLint linter.dupNamespace id + m!"The namespaces {ns} are duplicated in the declaration \ + `{.ofConstName (fullNames := true) declName}`." initialize addLinter dupNamespace diff --git a/Mathlib/Tactic/Linter/Style.lean b/Mathlib/Tactic/Linter/Style.lean index 5b17726d37d547..d949f89d541593 100644 --- a/Mathlib/Tactic/Linter/Style.lean +++ b/Mathlib/Tactic/Linter/Style.lean @@ -458,14 +458,14 @@ def longLineLinter : Linter where run := withSetOptionIn fun stx ↦ do return if stx.isOfKind ``Lean.Parser.Module.header then return -- if the linter reached the end of the file, then we scan the `import` syntax instead - let stx := ← do + let stx ← do if stx.isOfKind ``Lean.Parser.Command.eoi then let fileMap ← getFileMap -- `impMods` is the syntax for the modules imported in the current file let (impMods, _) ← Parser.parseHeader { inputString := fileMap.source, fileName := ← getFileName, fileMap := fileMap } - return impMods.raw - else return stx + pure impMods.raw + else pure stx let sstr := stx.getSubstring? let fm ← getFileMap let maxLineLength := linter.style.longLine.maxLineLength.get (← getOptions) diff --git a/Mathlib/Tactic/Linter/ValidatePRTitle.lean b/Mathlib/Tactic/Linter/ValidatePRTitle.lean index feb10809e868a7..1fb45d557e4a16 100644 --- a/Mathlib/Tactic/Linter/ValidatePRTitle.lean +++ b/Mathlib/Tactic/Linter/ValidatePRTitle.lean @@ -11,6 +11,7 @@ import Mathlib.Tactic.Linter.TextBased.UnicodeLinter /-! # Checker for well-formed title and labels + This script checks if a PR title matches [mathlib's commit conventions](https://leanprover-community.github.io/contribute/commit.html). Not all checks from the commit conventions are implemented: for instance, no effort is made to @@ -69,6 +70,12 @@ def prTitle : Parser (String × Option String × String) := do #guard_msgs in #eval Parser.run prTitle "chore: test" +/-- +Check if `word` looks like an abbreviation, like `JSON` or `E2` or `W3C`. +-/ +def isAbbreviation (word : String.Slice) : Bool := + word.all (fun c => c.isUpper || c.isDigit) && word.chars.length != 1 + open Mathlib.Linter.TextBased in /-- Check if `title` matches the mathlib conventions for PR titles @@ -112,11 +119,17 @@ public def validateTitle (title : String) : Array String := Id.run do -- Future: we could check if `scope` describes a directory that actually exist. -- Should we allow special syntax such as `Data/*/Basic` or `{Set,Group}Theory`? - -- Titles should be lower-cased (but we allow abbreviations). + -- Titles should be lower-cased (but we allow abbreviations, or a `s` or `'ed` suffix). if subject.front.toLower != subject.front then let firstWord := subject.takeWhile (!·.isWhitespace) - if !(firstWord.all (·.isUpper)) then - errors := errors.push "error: the PR subject should be lowercased" + let suffixes := ["'s", "s", "'ed"] + let mut withoutSuffix := firstWord + for suff in suffixes do + if firstWord.endsWith suff then + withoutSuffix := firstWord.dropSuffix suff + break + if !(isAbbreviation withoutSuffix) then + errors := errors.push s!"error: the PR subject `{subject}` should be lowercased" if subject.endsWith "." then errors := errors.push "error: the PR title should not end with a full stop" else if subject.endsWith " " then diff --git a/Mathlib/Tactic/Linter/Whitespace.lean b/Mathlib/Tactic/Linter/Whitespace.lean index 16a175642c7990..2c6a496e390461 100644 --- a/Mathlib/Tactic/Linter/Whitespace.lean +++ b/Mathlib/Tactic/Linter/Whitespace.lean @@ -335,14 +335,14 @@ def whitespaceLinter : Linter where run := withSetOptionIn fun stx ↦ do return let some upTo := CommandStart.endPos stx | return - let fmt : Option Format := ← + let fmt : Option Format ← try - liftCoreM <| PrettyPrinter.ppCategory `command stx + liftCoreM <| some <$> PrettyPrinter.ppCategory `command stx catch _ => Linter.logLintIf linter.style.whitespace.verbose (stx.getHead?.getD stx) m!"The `whitespace` linter had some parsing issues: \ feel free to silence it and report this error!" - return none + pure none if let some fmt := fmt then let st := fmt.pretty let origSubstring := stx.getSubstring?.getD default diff --git a/Mathlib/Tactic/NormNum/Core.lean b/Mathlib/Tactic/NormNum/Core.lean index be79b82e34957e..a82d5d3ca3e269 100644 --- a/Mathlib/Tactic/NormNum/Core.lean +++ b/Mathlib/Tactic/NormNum/Core.lean @@ -151,7 +151,7 @@ and returning the truth or falsity of `p' : Prop` from an equivalence `p ↔ p'` def deriveBoolOfIff (p p' : Q(Prop)) (hp : Q($p ↔ $p')) : MetaM ((b : Bool) × BoolResult p' b) := do let ⟨b, pb⟩ ← deriveBool p - match b with + match (dependent := true) b with | true => return ⟨true, q(Iff.mp $hp $pb)⟩ | false => return ⟨false, q((Iff.not $hp).mp $pb)⟩ @@ -275,11 +275,11 @@ open Lean.Parser.Tactic Meta.NormNum `ℕ`, `ℤ`, `ℚ`, `ℝ`, `ℂ`. In addition to evaluating numerical expressions, `norm_num` will use `simp` to simplify the goal. If the goal has the form `A = B`, `A ≠ B`, `A < B` or `A ≤ B`, where `A` and `B` are numerical expressions, `norm_num` will try to close it. It also has a relatively simple -primality prover. +primality prover (available if you import `Mathlib.Tactic.NormNum.Prime`). This tactic is extensible. Extensions can allow `norm_num` to evaluate more kinds of expressions, or -to prove more kinds of propositions. See the `@[norm_num]` attribute for further information on -extending `norm_num`. +to prove more kinds of propositions (such as, primality of natural numbers). See the `@[norm_num]` +attribute for further information on extending `norm_num`. * `norm_num at l` normalizes at location(s) `l`. * `norm_num [h1, ...]` adds the arguments `h1, ...` to the `simp` set in addition to the default diff --git a/Mathlib/Tactic/NormNum/Irrational.lean b/Mathlib/Tactic/NormNum/Irrational.lean index 8528c4739b4e17..e062206ec53c36 100644 --- a/Mathlib/Tactic/NormNum/Irrational.lean +++ b/Mathlib/Tactic/NormNum/Irrational.lean @@ -260,64 +260,64 @@ def findNotPowerCertificate (m n : Q(ℕ)) : MetaM (NotPowerCertificate m n) := /-- `norm_num` extension that proves `Irrational x ^ y` for rational `y`. `x` may be natural or rational. -/ @[norm_num Irrational (_ ^ (_ : ℝ))] -def evalIrrationalRpow : NormNumExt where eval {u α} e := do - let 0 := u | failure - let ~q(Prop) := α | failure - let ~q(Irrational (($x : ℝ) ^ ($y : ℝ))) := e | failure - let .isNNRat sℝ _ y_num y_den y_isNNRat ← derive y | failure - let ⟨gy, hy_coprime⟩ := proveNatGCD y_num y_den - if gy.natLit! != 1 then failure - let _ : $gy =Q 1 := ⟨⟩ - match ← derive x with - | .isNat sℝ ex x_isNat => - let cert ← findNotPowerCertificate q($ex) y_den - assumeInstancesCommute - return .isTrue q(irrational_rpow_nat_rat $x_isNat $y_isNNRat $hy_coprime - $cert.pf_left $cert.pf_right) - | .isNNRat sℝ _ x_num x_den x_isNNRat => - let ⟨gx, hx_coprime⟩ := proveNatGCD x_num x_den - if gx.natLit! != 1 then failure - let _ : $gx =Q 1 := ⟨⟩ - let hx_isNNRat' : Q(IsNNRat $x $x_num $x_den) := x_isNNRat - let hy_isNNRat' : Q(IsNNRat $y $y_num $y_den) := y_isNNRat - try - let numCert ← findNotPowerCertificate q($x_num) y_den +def evalIrrationalRpow : NormNumExt where eval {u α} e := + match u, α, e with + | 0, ~q(Prop), ~q(Irrational (($x : ℝ) ^ ($y : ℝ))) => do + let .isNNRat sℝ _ y_num y_den y_isNNRat ← derive y | failure + let ⟨gy, hy_coprime⟩ := proveNatGCD y_num y_den + if gy.natLit! != 1 then failure + let _ : $gy =Q 1 := ⟨⟩ + match ← derive x with + | .isNat sℝ ex x_isNat => + let cert ← findNotPowerCertificate q($ex) y_den assumeInstancesCommute - return Result.isTrue q(irrational_rpow_rat_rat_of_num $hx_isNNRat' $hy_isNNRat' - $hx_coprime $hy_coprime $numCert.pf_left $numCert.pf_right) - catch _ => - let denCert ← findNotPowerCertificate q($x_den) y_den - assumeInstancesCommute - return Result.isTrue q(irrational_rpow_rat_rat_of_den $hx_isNNRat' $hy_isNNRat' - $hx_coprime $hy_coprime $denCert.pf_left $denCert.pf_right) - | _ => failure + return .isTrue q(irrational_rpow_nat_rat $x_isNat $y_isNNRat $hy_coprime + $cert.pf_left $cert.pf_right) + | .isNNRat sℝ _ x_num x_den x_isNNRat => + let ⟨gx, hx_coprime⟩ := proveNatGCD x_num x_den + if gx.natLit! != 1 then failure + let _ : $gx =Q 1 := ⟨⟩ + let hx_isNNRat' : Q(IsNNRat $x $x_num $x_den) := x_isNNRat + let hy_isNNRat' : Q(IsNNRat $y $y_num $y_den) := y_isNNRat + try + let numCert ← findNotPowerCertificate q($x_num) y_den + assumeInstancesCommute + return Result.isTrue q(irrational_rpow_rat_rat_of_num $hx_isNNRat' $hy_isNNRat' + $hx_coprime $hy_coprime $numCert.pf_left $numCert.pf_right) + catch _ => + let denCert ← findNotPowerCertificate q($x_den) y_den + assumeInstancesCommute + return Result.isTrue q(irrational_rpow_rat_rat_of_den $hx_isNNRat' $hy_isNNRat' + $hx_coprime $hy_coprime $denCert.pf_left $denCert.pf_right) + | _ => failure + | _, _, _ => failure /-- `norm_num` extension that proves `Irrational √x` for rational `x`. -/ @[norm_num Irrational (Real.sqrt _)] def evalIrrationalSqrt : NormNumExt where eval {u α} e := do - let 0 := u | failure - let ~q(Prop) := α | failure - let ~q(Irrational (√$x)) := e | failure - match ← derive x with - | .isNat sℝ ex pf => - let cert ← findNotPowerCertificate ex q(nat_lit 2) - assumeInstancesCommute - return .isTrue q(irrational_sqrt_nat $pf $cert.pf_left $cert.pf_right) - | .isNNRat sℝ eq en ed pf => - let ⟨g, pf_coprime⟩ := proveNatGCD en ed - if g.natLit! != 1 then failure - let _ : $g =Q 1 := ⟨⟩ - try - let numCert ← findNotPowerCertificate en q(nat_lit 2) - assumeInstancesCommute - return Result.isTrue - q(irrational_sqrt_rat_of_num $pf $pf_coprime $numCert.pf_left $numCert.pf_right) - catch _ => - let denCert ← findNotPowerCertificate ed q(nat_lit 2) + match u, α, e with + | 0, ~q(Prop), ~q(Irrational (√$x)) => do + match ← derive x with + | .isNat sℝ ex pf => + let cert ← findNotPowerCertificate ex q(nat_lit 2) assumeInstancesCommute - return Result.isTrue - q(irrational_sqrt_rat_of_den $pf $pf_coprime $denCert.pf_left $denCert.pf_right) - | _ => failure + return .isTrue q(irrational_sqrt_nat $pf $cert.pf_left $cert.pf_right) + | .isNNRat sℝ eq en ed pf => + let ⟨g, pf_coprime⟩ := proveNatGCD en ed + if g.natLit! != 1 then failure + let _ : $g =Q 1 := ⟨⟩ + try + let numCert ← findNotPowerCertificate en q(nat_lit 2) + assumeInstancesCommute + return Result.isTrue + q(irrational_sqrt_rat_of_num $pf $pf_coprime $numCert.pf_left $numCert.pf_right) + catch _ => + let denCert ← findNotPowerCertificate ed q(nat_lit 2) + assumeInstancesCommute + return Result.isTrue + q(irrational_sqrt_rat_of_den $pf $pf_coprime $denCert.pf_left $denCert.pf_right) + | _ => failure + | _, _, _ => failure end NormNum diff --git a/Mathlib/Tactic/Polynomial/Basic.lean b/Mathlib/Tactic/Polynomial/Basic.lean new file mode 100644 index 00000000000000..c2c74e5d77e67f --- /dev/null +++ b/Mathlib/Tactic/Polynomial/Basic.lean @@ -0,0 +1,179 @@ +/- +Copyright (c) 2025 Arend Mellendijk. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Arend Mellendijk +-/ + +module + +public import Mathlib.Algebra.Polynomial.AlgebraMap +public import Mathlib.Algebra.Polynomial.Coeff +public import Mathlib.Tactic.Algebra.Basic +public import Mathlib.Tactic.Algebra.AlgebraNF +public import Mathlib.Tactic.Polynomial.Core + +/-! +# Polynomial +An extensible tactic for proving equality of polynomial expressions implemented using `algebra`. +To add support for a new polynomial-like type, one needs to do three things: +* Implement a polynomial extension that lets `polynomial` infer the base ring from the algebraic + type. For example: +``` +@[polynomial_infer_base] +def polynomialInferBase : PolynomialExt where + infer e := do + match_expr e with + | Polynomial R _ => pure R + | _ => failure +``` +* Tag any preprocessing lemmas with @[polynomial_pre]. This would include a lemma saying that +`C = algebraMap _ _` so that `algebra` knows how to normalize it. +* Tag any postprocessing lemmas with @[polynomial_post], so that `polynomial_nf` produces a pretty +expression. +-/ + +open Lean Mathlib.Tactic Mathlib.Tactic.Algebra Parser.Tactic Elab Meta Qq + +public meta section + +namespace Mathlib.Tactic.Polynomial + +/-- Infer base ring for `Polynomial R` -/ +@[polynomial_infer_base] +def polynomialInferBase : PolynomialExt where + infer e := do + match_expr e with + | Polynomial R _ => pure R + | _ => failure + +section Lemmas + +variable {σ R A : Type*} [CommSemiring R] [CommSemiring A] [Algebra R A] + +attribute [polynomial_post] mul_one Algebra.smul_def Polynomial.algebraMap_eq + +@[polynomial_pre] +theorem monomial_eq_smul (a : R) (n : ℕ) : Polynomial.monomial n a = a • (.X ^ n) := by + rw [← Polynomial.C_mul_X_pow_eq_monomial, Polynomial.smul_eq_C_mul] + +-- `polynomial_pre` contains a lemma sending `C -> algebraMap`, so `C` is not simp normal form. +@[polynomial_pre] +theorem map_algebraMap (r : R) : + Polynomial.map (algebraMap R A) (algebraMap R (Polynomial R) r) = + algebraMap A (Polynomial A) (algebraMap R A r) := by + simp + +end Lemmas + +open Mathlib.Meta AtomM + +attribute [polynomial_pre] Polynomial.C_eq_algebraMap + Polynomial.monomial_eq_smul Polynomial.map_add Polynomial.map_mul Polynomial.map_pow + Polynomial.map_X Polynomial.map_natCast Polynomial.map_intCast + +/- TODO: we don't currently have a good way to normalize monomials of MvPolynomials. These are +indexed by finsupps, making it difficult to turn into the appropriate normal form. -/ +/-- Run the `polynomial_pre` simpset to turn nonstandard spellings of `algebraMap` such as +`Polynomial.C` into `algebraMap` -/ +def preprocess (e : Expr) : MetaM Simp.Result := do + let preThms ← polynomialPreExt.getTheorems + let ctx ← Simp.mkContext { failIfUnchanged := false } (simpTheorems := #[preThms]) + pure (← Simp.main e ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1 + +open Tactic + +/-- `polynomial` solves equalities of `Polynomial`s and similar types. + +Given a goal which is an equality in `Polynomial R` with a commutative ring `R`, `polynomial` +turns both sides of the equation into a normal form by expanding out the brackets. It then closes +the goal if both sides contain the same terms and fails otherwise. `polynomial_nf` normalizes all +subexpressions at a given location. + +Variants of `polynomial` include: +* `polynomial`: normalize both sides of an equation and close the goal if they are equal +* `polynomial!`: run `polynomial` at default transparency +* `polynomial_nf`: normalize all subexpression of the goal +* `polynomial_nf at h₁ h₂ ⊢`: normalize all subexpressions at hypotheses `h₁` `h₁` and the goal +* `polynomial_nf at *`: normalize all subexpressions of all local hypotheses and the goal +* `polynomial_nf!`: run `polynomial_nf` at default transparency + +The `polynomial` tactic can be extended to work with algebras other than `Polynomial` using the +attributes `polynomial_infer_base`, `polynomial_pre` and `polynomial_post`. This is only possible +if the base ring can be inferred from the structure of the type. + +Examples: + +``` +example (a : ℚ) : (X + C a) * (X - C a) = X^2 + C (a^2) := by polynomial + +example {P : ℚ[X] → Prop} (h : P (X ^ 2 + X + C 4⁻¹)) : P ((X + C 2⁻¹) ^ 2) := by + polynomial_nf at h ⊢ + exact h +``` + +-/ +elab (name := polynomial) "polynomial" tk:"!"? : tactic => + withMainContext do + let g ← getMainGoal + let some (α, _, _) := (← whnfR <|← instantiateMVars <|← g.getType).eq? + | throwError "polynomial failed: not an equality" + let mut β : Expr := default + try + β ← Polynomial.inferBase α + catch _ => + throwError "polynomial failed: not an equality of (mv)polynomials" + let some g ← transformAtTarget (fun e _ ↦ Polynomial.preprocess e) "polynomial" .silent g + default | done + let some g ← transformAtTarget (fun e _ ↦ Algebra.preprocess e) "polynomial" .silent g + default | done + AtomM.run (if tk.isSome then .default else .reducible) + (Algebra.proveEq (some (← getLevelQ' β)) g) + +@[tactic_alt polynomial] +macro "polynomial!" : tactic => `(tactic| polynomial !) + +/-- A cleanup routine, which simplifies normalized expressions to a more human-friendly format. +This is the `algebra_nf` cleanup routine with a little extra work to turn scalar multiplication +into `(MV)Polynomial.C` -/ +def cleanup (cfg : RingNF.Config) (r : Simp.Result) : MetaM Simp.Result := do + match cfg.mode with + | .raw => pure r + | .SOP => do + let r ← cleanupSMul cfg r + let thms : SimpTheorems ← polynomialPostExt.getTheorems + let ctx ← Simp.mkContext { zetaDelta := cfg.zetaDelta } + (simpTheorems := #[thms]) + (congrTheorems := ← getSimpCongrTheorems) + pure <| ← + r.mkEqTrans (← Simp.main r.expr ctx (methods := Lean.Meta.Simp.mkDefaultMethodsCore {})).1 + +/-- Normalize a polynomial expression into standard form. Used by `polynomial_nf`. -/ +def evalExprPoly (e : Expr) : AtomM Simp.Result := do + let ⟨_, α, e⟩ ← inferTypeQ e + let mut R : Expr := default + try R ← inferBase α + catch _ => throwError "not a polynomial" + let r₁ ← Polynomial.preprocess e + let r₂ ← Algebra.preprocess r₁.expr + let ⟨_, R'⟩ ← getLevelQ' R + let r₃ ← evalExpr R' r₂.expr + (← r₁.mkEqTrans r₂).mkEqTrans r₃ + +@[tactic_alt polynomial] +elab (name := polynomialNF) "polynomial_nf" tk:"!"? loc:(location)? : tactic => withMainContext do + let mut cfg := {} + if tk.isSome then cfg := { cfg with red := .default, zetaDelta := true } + let loc := (loc.map expandLocation).getD (.targets #[] true) + let s ← IO.mkRef {} + let m := AtomM.recurse s cfg.toConfig (wellBehavedDischarge := true) (evalExprPoly) (cleanup cfg) + transformAtLocation (m ·) "polynomial_nf" loc cfg.ifUnchanged false + +@[tactic_alt polynomial] +macro "polynomial_nf!" loc:(location)? : tactic => + `(tactic| polynomial_nf ! $(loc)?) + +end Mathlib.Tactic.Polynomial + +open Polynomial + +end diff --git a/Mathlib/Tactic/Polynomial/Core.lean b/Mathlib/Tactic/Polynomial/Core.lean new file mode 100644 index 00000000000000..1c7ab0c3e182c1 --- /dev/null +++ b/Mathlib/Tactic/Polynomial/Core.lean @@ -0,0 +1,100 @@ +/- +Copyright (c) 2025 Arend Mellendijk. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Arend Mellendijk +-/ +module + +meta import Lean.Compiler.IR.CompilerM +public meta import Lean.Meta.Tactic.Simp.Attr +public import Mathlib.Init + +/-! +# Setup for the `polynomial` tactic + +This file initializes the environment extensions and simp sets used by the `polynomial` tactic. + +These extensions let downstream users use their own polynomial-like types (such as `PowerSeries`) +with the `polynomial` tactic suite. +-/ + +namespace Mathlib.Tactic.Polynomial + +open Lean Lean.Meta Lean.Elab Term + +public meta section + +/-- `polynomial_pre` marks a theorem to be used by the `polynomial` tactic as a preprocessing lemma. +These serve the purpose of removing any definitions specific to polynomials that `algebra` can't +handle. e.g. `Polynomial.C` and `Polynomial.map` -/ +initialize polynomialPreExt : SimpExtension ← + registerSimpAttr `polynomial_pre "\ + The `polynomial_pre` simp attribute uses preprocessing lemmas \ + to turn specialized functions into `algebraMap`s" + +/-- `polynomial_post` marks a theorem to be used by the `polynomial_nf` tactic as a postprocessing +lemma. Used only by polynomial_nf. These serve the purpose of rewriting expressions in `algebra` +normal form into a more readable form. e.g. `a • X` -> `algebraMap _ _ a * X` -> `C a * X`. -/ +initialize polynomialPostExt : SimpExtension ← + registerSimpAttr `polynomial_post "\ + The `polynomial_post` simp attribute uses postprocessing lemmas \ + to turn `algebraMap`s into more specialized functions." + +/-- `polynomial_infer_base` marks a procedure used by the `polynomial` tactic to infer +the base ring of polynomial-like types. -/ +syntax (name := PolyInferBaseAttr) "polynomial_infer_base" : attr + +/-- An extension for `polynomial`. -/ +structure PolynomialExt where + /-- Attempts to infer the base `R` of an `Algebra R A` based only on `A`. e.g. returns `R` given + `Polynomial R`. -/ + infer : Expr → MetaM Expr + +/-- Read a `polynomial` extension from a declaration of the right type. -/ +def mkPolynomialExt (n : Name) : ImportM PolynomialExt := do + let { env, opts, .. } ← read + IO.ofExcept <| unsafe env.evalConstCheck PolynomialExt opts ``PolynomialExt n + +/-- Environment extensions for `polynomial` declarations -/ +initialize polynomialExt : PersistentEnvExtension Name (Name × PolynomialExt) + (List Name × List (Name × PolynomialExt)) ← + registerPersistentEnvExtension { + mkInitial := pure ([], {}) + addImportedFn := fun s => do + let dt ← s.foldlM (init := {}) fun dt s => s.foldlM (init := dt) fun dt n => do + return (n, ← mkPolynomialExt n) :: dt + pure ([], dt) + addEntryFn := fun (entries, s) (n, ext) => (n :: entries, (n, ext) :: s) + exportEntriesFn := fun s => s.1.reverse.toArray + } + +initialize registerBuiltinAttribute { + name := `PolyInferBaseAttr + descr := "adds a polynomial extension that infers the base ring of a polynomial-like type" + applicationTime := .afterCompilation + add := fun declName stx kind => match stx with + | `(attr| polynomial_infer_base) => do + unless kind == AttributeKind.global do + throwError "invalid attribute 'polynomial_infer_base', must be global" + let env ← getEnv + unless (env.getModuleIdxFor? declName).isNone do + throwError "invalid attribute 'polynomial_infer_base', declaration is in an imported module" + if (IR.getSorryDep env declName).isSome then return -- ignore in progress definitions + let ext ← mkPolynomialExt declName + setEnv <| polynomialExt.addEntry env (declName, ext) + | _ => throwUnsupportedSyntax +} + +/-- Infer the base ring of `Polynomial`-like types that are registered using the `polynomial` +environment extensions. Includes e.g. `Polynomial` and `MvPolynomial`. -/ +def inferBase (e : Expr) : MetaM Expr := do + for ⟨_, ext⟩ in (polynomialExt.getState (← getEnv)).2 do + try + return ← ext.infer e + catch _ => + continue + failure + +end + +end Mathlib.Tactic.Polynomial diff --git a/Mathlib/Tactic/Positivity/Basic.lean b/Mathlib/Tactic/Positivity/Basic.lean index 4e078e63ad35fc..0efd17c7fde774 100644 --- a/Mathlib/Tactic/Positivity/Basic.lean +++ b/Mathlib/Tactic/Positivity/Basic.lean @@ -63,6 +63,7 @@ such that `positivity` successfully recognises both `a` and `b`. -/ haveI' : $e =Q ite $p $a $b := ⟨⟩ let ra ← core zα pα? a; let rb ← core zα pα? b guard <|← withDefault <| withNewMCtxDepth <| isDefEq f q(ite (α := $α)) + id <| match ra, rb with | .positive pa, .positive pb => pure (.positive q(ite_pos $p $pa $pb)) | .positive pa, .nonnegative pb => pure (.nonnegative q(ite_nonneg_of_pos_of_nonneg $p $pa $pb)) @@ -95,7 +96,7 @@ such that `positivity` successfully recognises both `a` and `b`. -/ let _a ← synthInstanceQ q(LinearOrder $α) let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(min) assumeInstancesCommute - match ← core zα pα? a, ← core zα pα? b with + match (dependent := true) ← core zα pα? a, ← core zα pα? b with | .positive (pα := pα') pa, .positive pb => assumeInstancesCommute pure (.positive q(lt_min $pa $pb)) @@ -127,7 +128,7 @@ is nonnegative, strictly positive if at least one is positive, and nonzero if bo let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(max) let result : Strictness zα e pα? ← catchNone do let ra ← core zα pα? a - match ra with + match (dependent := true) ra with | .positive pa => assumeInstancesCommute pure (.positive q(lt_max_of_lt_left $pa)) @@ -140,7 +141,7 @@ is nonnegative, strictly positive if at least one is positive, and nonzero if bo | _ => pure .none orElse result do let rb ← core zα pα? b - match rb with + match (dependent := true) rb with | .positive pb => assumeInstancesCommute pure (.positive q(lt_max_of_lt_right $pb)) @@ -155,14 +156,14 @@ is nonnegative, strictly positive if at least one is positive, and nonzero if bo /-- The `positivity` extension which identifies expressions of the form `a + b`, such that `positivity` successfully recognises both `a` and `b`. -/ -@[positivity _ + _] def evalAdd : PositivityExt where eval {u α} zα pα? e := do +@[positivity _ + _] def evalAdd : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do let .app (.app (f : Q($α → $α → $α)) (a : Q($α))) (b : Q($α)) ← whnfR e | throwError "not +" let _e_eq : $e =Q $f $a $b := ⟨⟩ let _a ← synthInstanceQ q(AddZeroClass $α) assumeInstancesCommute let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HAdd.hAdd) - let ra ← core zα pα? a; let rb ← core zα pα? b - let some _pα := pα? | pure .none + let ra ← core zα pα a; let rb ← core zα pα b match ra, rb with | .positive pa, .positive pb => let _a ← synthInstanceQ q(AddLeftMono $α) @@ -186,8 +187,9 @@ such that there is a local hypothesis `b < a`, `b ≤ a`, `a ≠ b` or `b ≠ a` let _a ← synthInstanceQ q(AddGroup $α) assumeInstancesCommute let ⟨_f_eq⟩ ← withDefault <| withNewMCtxDepth <| assertDefEqQ q($f) q(HSub.hSub) + id <| match pα? with - | some pα => + | some pα => do let mut result := .none for decl in ← getLCtx do unless decl.isImplementationDetail do @@ -221,7 +223,7 @@ such that there is a local hypothesis `b < a`, `b ≤ a`, `a ≠ b` or `b ≠ a` return .none | _ => return .none return result - | none => + | none => do let mut result := .none for decl in ← getLCtx do unless decl.isImplementationDetail do @@ -271,8 +273,9 @@ such that `positivity` successfully recognises both `a` and `b`. -/ let _a ← synthInstanceQ q(PosMulStrictMono $α) assumeInstancesCommute pure (.positive q(mul_pos $pa $pb)) + id <| match pα? with - | some pα => + | some pα => do let mut result : Strictness zα e (some pα) := .none result ← orElse result (tryProvePositive pα ra.toPositive rb.toPositive) result ← orElse result (tryProveNonneg pα ra.toNonneg rb.toNonneg) @@ -295,8 +298,8 @@ lemma int_div_nonneg_of_pos_of_pos {a b : ℤ} (ha : 0 < a) (hb : 0 < b) : 0 ≤ /-- The `positivity` extension which identifies expressions of the form `a / b`, where `a` and `b` are integers. -/ -@[positivity (_ : ℤ) / (_ : ℤ)] def evalIntDiv : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℤ" +@[positivity (_ : ℤ) / (_ : ℤ)] def evalIntDiv : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℤ), ~q($a / $b) => let ra ← core q(inferInstance) (some q(inferInstance)) a @@ -334,78 +337,82 @@ meta def evalPowZeroNat : PositivityExt where eval {u α} _zα pα? e := do assumeInstancesCommute haveI' : $e =Q $a ^ 0 := ⟨⟩ let _a ← synthInstanceQ q(Nontrivial $α) - let some _pα := pα? | pure (.nonzero q(pow_zero_ne_zero $a)) - let _a ← synthInstanceQ q(IsOrderedRing $α) - pure (.positive q(pow_zero_pos $a)) + match (dependent := true) pα? with + | some _pα => + let _a ← synthInstanceQ q(IsOrderedRing $α) + pure (.positive q(pow_zero_pos $a)) + | none => pure (.nonzero q(pow_zero_ne_zero $a)) /-- The `positivity` extension which identifies expressions of the form `a ^ (b : ℕ)`, such that `positivity` successfully recognises both `a` and `b`. -/ @[positivity _ ^ (_ : ℕ)] meta def evalPow : PositivityExt where eval {u α} zα pα? e := do let .app (.app _ (a : Q($α))) (b : Q(ℕ)) ← whnfR e | throwError "not ^" - let some pα := pα? | do + match (dependent := true) pα? with + | none => let _a ← synthInstanceQ q(MonoidWithZero $α) let _a ← synthInstanceQ q(NoZeroDivisors $α) assumeInstancesCommute haveI' : $e =Q $a ^ $b := ⟨⟩ let .nonzero nza ← core zα .none a | pure .none pure (.nonzero q(pow_ne_zero $b $nza)) - let result : Strictness zα e pα ← catchNone do - let _a ← synthInstanceQ q(Ring $α) - let _a ← synthInstanceQ q(LinearOrder $α) - let _a ← synthInstanceQ q(IsStrictOrderedRing $α) - assumeInstancesCommute - let .true := b.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ n where n is a literal" - let some n := (b.getRevArg! 1).rawNatLit? | throwError "not a ^ n where n is a literal" - guard (n % 2 = 0) - have m : Q(ℕ) := mkRawNatLit (n / 2) - haveI' : $b =Q 2 * $m := ⟨⟩ - haveI' : $e =Q $a ^ $b := ⟨⟩ - pure (.nonnegative q((even_two_mul $m).pow_nonneg $a)) - orElse result do - let ra ← core zα pα a - let ofNonneg (pa : Q(0 ≤ $a)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) : - MetaM (Strictness zα e (some pα)) := do - haveI' : $e =Q $a ^ $b := ⟨⟩ + | some pα => + let result : Strictness zα e pα ← catchNone do + let _a ← synthInstanceQ q(Ring $α) + let _a ← synthInstanceQ q(LinearOrder $α) + let _a ← synthInstanceQ q(IsStrictOrderedRing $α) assumeInstancesCommute - pure (.nonnegative q(pow_nonneg $pa $b)) - let ofNonzero (pa : Q($a ≠ 0)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) : - MetaM (Strictness zα e (some pα)) := do + let .true := b.isAppOfArity ``OfNat.ofNat 3 | throwError "not a ^ n where n is a literal" + let some n := (b.getRevArg! 1).rawNatLit? | throwError "not a ^ n where n is a literal" + guard (n % 2 = 0) + have m : Q(ℕ) := mkRawNatLit (n / 2) + haveI' : $b =Q 2 * $m := ⟨⟩ haveI' : $e =Q $a ^ $b := ⟨⟩ - assumeInstancesCommute - let _a ← synthInstanceQ q(NoZeroDivisors $α) - pure (.nonzero q(pow_ne_zero $b $pa)) - match ra with - | .positive pa => - try - let _a ← synthInstanceQ q(Semiring $α) - let _a ← synthInstanceQ q(IsStrictOrderedRing $α) + pure (.nonnegative q((even_two_mul $m).pow_nonneg $a)) + orElse result do + let ra ← core zα pα a + let ofNonneg (pa : Q(0 ≤ $a)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) : + MetaM (Strictness zα e (some pα)) := do + haveI' : $e =Q $a ^ $b := ⟨⟩ assumeInstancesCommute + pure (.nonnegative q(pow_nonneg $pa $b)) + let ofNonzero (pa : Q($a ≠ 0)) (_rα : Q(Semiring $α)) (_oα : Q(IsOrderedRing $α)) : + MetaM (Strictness zα e (some pα)) := do haveI' : $e =Q $a ^ $b := ⟨⟩ - pure (.positive q(pow_pos $pa $b)) - catch e : Exception => - trace[Tactic.positivity.failure] "{e.toMessageData}" - let rα ← synthInstanceQ q(Semiring $α) - let oα ← synthInstanceQ q(IsOrderedRing $α) - orElse (← catchNone (ofNonneg q(le_of_lt $pa) rα oα)) (ofNonzero q(ne_of_gt $pa) rα oα) - | .nonnegative pa => - let sα ← synthInstanceQ q(Semiring $α) - let oα ← synthInstanceQ q(IsOrderedRing $α) - ofNonneg q($pa) q($sα) q($oα) - | .nonzero pa => - let sα ← synthInstanceQ q(Semiring $α) - let oα ← synthInstanceQ q(IsOrderedRing $α) - ofNonzero q($pa) q($sα) q($oα) - | .none => pure .none + assumeInstancesCommute + let _a ← synthInstanceQ q(NoZeroDivisors $α) + pure (.nonzero q(pow_ne_zero $b $pa)) + match ra with + | .positive pa => + try + let _a ← synthInstanceQ q(Semiring $α) + let _a ← synthInstanceQ q(IsStrictOrderedRing $α) + assumeInstancesCommute + haveI' : $e =Q $a ^ $b := ⟨⟩ + pure (.positive q(pow_pos $pa $b)) + catch e : Exception => + trace[Tactic.positivity.failure] "{e.toMessageData}" + let rα ← synthInstanceQ q(Semiring $α) + let oα ← synthInstanceQ q(IsOrderedRing $α) + orElse (← catchNone (ofNonneg q(le_of_lt $pa) rα oα)) (ofNonzero q(ne_of_gt $pa) rα oα) + | .nonnegative pa => + let sα ← synthInstanceQ q(Semiring $α) + let oα ← synthInstanceQ q(IsOrderedRing $α) + ofNonneg q($pa) q($sα) q($oα) + | .nonzero pa => + let sα ← synthInstanceQ q(Semiring $α) + let oα ← synthInstanceQ q(IsOrderedRing $α) + ofNonzero q($pa) q($sα) q($oα) + | .none => pure .none theorem abs_pos_of_ne_zero {α : Type*} [AddGroup α] [LinearOrder α] [AddLeftMono α] {a : α} : a ≠ 0 → 0 < |a| := abs_pos.mpr /-- The `positivity` extension which identifies expressions of the form `|a|`. -/ @[positivity |_|] -meta def evalAbs : PositivityExt where eval {_u} (α zα pα?) (e : Q($α)) := do +meta def evalAbs : PositivityExt where eval {_u} (α zα pα?) (e : Q($α)) := + match pα? with | none => pure .none | some pα' => do let ~q(@abs _ (_) (_) $a) := e | throwError "not |·|" - let some pα' := pα? | pure .none try match ← core zα (some pα') a with | .positive pa => @@ -427,8 +434,8 @@ Since the output type of `Int.natAbs` is `ℕ`, the nonnegative case is handled `positivity` tactic. -/ @[positivity Int.natAbs _] -meta def evalNatAbs : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalNatAbs : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Int.natAbs $a) => let zα' : Q(Zero Int) := q(inferInstance) @@ -453,25 +460,27 @@ meta def evalNatCast : PositivityExt where eval {u α} _zα pα? e := do let ~q(@Nat.cast _ (_) ($a : ℕ)) := e | throwError "not Nat.cast" let zα' : Q(Zero Nat) := q(inferInstance) let (_i1 : Q(AddMonoidWithOne $α)) ← synthInstanceQ q(AddMonoidWithOne $α) - let some _pα := pα? | do + match (dependent := true) pα? with + | none => let (_cz : Q(CharZero $α)) ← synthInstanceQ q(CharZero $α) assumeInstancesCommute match ← core zα' .none a with | .nonzero nza => pure (.nonzero q(Nat.cast_ne_zero.2 $nza)) | _ => pure .none - let pα' : Q(PartialOrder Nat) := q(inferInstance) - let (_i2 : Q(AddLeftMono $α)) ← synthInstanceQ q(AddLeftMono $α) - let (_i3 : Q(ZeroLEOneClass $α)) ← synthInstanceQ q(ZeroLEOneClass $α) - assumeInstancesCommute - match ← core zα' pα' a with - | .positive pa => - try - let _nz ← synthInstanceQ q(NeZero (1 : $α)) - pure (.positive q(Nat.cast_pos'.2 $pa)) - catch _ => + | some _pα => + let pα' : Q(PartialOrder Nat) := q(inferInstance) + let (_i2 : Q(AddLeftMono $α)) ← synthInstanceQ q(AddLeftMono $α) + let (_i3 : Q(ZeroLEOneClass $α)) ← synthInstanceQ q(ZeroLEOneClass $α) + assumeInstancesCommute + match ← core zα' pα' a with + | .positive pa => + try + let _nz ← synthInstanceQ q(NeZero (1 : $α)) + pure (.positive q(Nat.cast_pos'.2 $pa)) + catch _ => + pure (.nonnegative q(Nat.cast_nonneg' _)) + | _ => pure (.nonnegative q(Nat.cast_nonneg' _)) - | _ => - pure (.nonnegative q(Nat.cast_nonneg' _)) /-- Extension for the `positivity` tactic: `Int.cast` is positive (resp. non-negative) if its input is. -/ @@ -481,7 +490,7 @@ meta def evalIntCast : PositivityExt where eval {u α} _zα pα? e := do let zα' : Q(Zero Int) := q(inferInstance) let pα' : Q(PartialOrder Int) := q(inferInstance) let ra ← core zα' pα' a - match ra, pα? with + match (dependent := true) ra, pα? with | .positive pa, some _ => let _rα ← synthInstanceQ q(Ring $α) let _oα ← synthInstanceQ q(IsOrderedRing $α) @@ -504,8 +513,8 @@ meta def evalIntCast : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for `Nat.succ`. -/ @[positivity Nat.succ _] -meta def evalNatSucc : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalNatSucc : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.succ $a) => assertInstancesCommute @@ -514,8 +523,8 @@ meta def evalNatSucc : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for `PNat.val`. -/ @[positivity PNat.val _] -meta def evalPNatVal : PositivityExt where eval {u α} _zα pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalPNatVal : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(PNat.val $a) => assertInstancesCommute @@ -524,8 +533,8 @@ meta def evalPNatVal : PositivityExt where eval {u α} _zα pα? e := do /-- Extension for `Nat.factorial`. -/ @[positivity Nat.factorial _] -meta def evalFactorial : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalFactorial : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.factorial $a) => assertInstancesCommute @@ -534,8 +543,8 @@ meta def evalFactorial : PositivityExt where eval {u α} _ pα? e := do /-- Extension for `Nat.ascFactorial`. -/ @[positivity Nat.ascFactorial _ _] -meta def evalAscFactorial : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalAscFactorial : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.ascFactorial ($n + 1) $k) => assertInstancesCommute @@ -547,7 +556,8 @@ Uses positivity of the left term, if available, then tries the right term. The implementation relies on the fact that `Positivity.core` on `ℕ` never returns `nonzero`. -/ @[positivity Nat.gcd _ _] -meta def evalNatGCD : PositivityExt where eval {u α} z p e := do +meta def evalNatGCD : PositivityExt where eval {u α} z p e := + match p with | none => throwError "not PartialOrder ℕ" | some p => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.gcd $a $b) => assertInstancesCommute @@ -565,7 +575,8 @@ meta def evalNatGCD : PositivityExt where eval {u α} z p e := do /-- Extension for `Nat.lcm`. -/ @[positivity Nat.lcm _ _] -meta def evalNatLCM : PositivityExt where eval {u α} z p e := do +meta def evalNatLCM : PositivityExt where eval {u α} z p e := + match p with | none => throwError "not PartialOrder ℕ" | some p => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.lcm $a $b) => match ← core z p a with @@ -581,7 +592,8 @@ meta def evalNatLCM : PositivityExt where eval {u α} z p e := do /-- Extension for `Nat.sqrt`. -/ @[positivity Nat.sqrt _] -meta def evalNatSqrt : PositivityExt where eval {u α} z p e := do +meta def evalNatSqrt : PositivityExt where eval {u α} z p e := + match p with | none => throwError "not PartialOrder ℕ" | some p => do match u, α, e with | 0, ~q(ℕ), ~q(Nat.sqrt $n) => match ← core z p n with @@ -594,8 +606,8 @@ meta def evalNatSqrt : PositivityExt where eval {u α} z p e := do /-- Extension for `Int.gcd`. Uses positivity of the left term, if available, then tries the right term. -/ @[positivity Int.gcd _ _] -meta def evalIntGCD : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalIntGCD : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Int.gcd $a $b) => let z ← synthInstanceQ (q(Zero ℤ) : Q(Type)) @@ -611,8 +623,8 @@ meta def evalIntGCD : PositivityExt where eval {u α} _ pα? e := do /-- Extension for `Int.lcm`. -/ @[positivity Int.lcm _ _] -meta def evalIntLCM : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalIntLCM : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Int.lcm $a $b) => let z ← synthInstanceQ (q(Zero ℤ) : Q(Type)) @@ -635,8 +647,8 @@ alias ⟨_, NNRat.num_ne_zero_of_ne_zero⟩ := num_ne_zero /-- The `positivity` extension which identifies expressions of the form `NNRat.num q`, such that `positivity` successfully recognises `q`. -/ @[positivity NNRat.num _] -meta def evalNNRatNum : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalNNRatNum : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(NNRat.num $a) => let zα : Q(Zero ℚ≥0) := q(inferInstance) @@ -652,8 +664,8 @@ meta def evalNNRatNum : PositivityExt where eval {u α} _ pα? e := do /-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/ @[positivity NNRat.den _] -meta def evalNNRatDen : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalNNRatDen : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(NNRat.den $a) => assumeInstancesCommute @@ -677,8 +689,8 @@ alias ⟨_, num_ne_zero_of_ne_zero⟩ := num_ne_zero /-- The `positivity` extension which identifies expressions of the form `Rat.num a`, such that `positivity` successfully recognises `a`. -/ @[positivity Rat.num _] -meta def evalRatNum : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℤ" +meta def evalRatNum : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℤ" | some _ => do match u, α, e with | 0, ~q(ℤ), ~q(Rat.num $a) => let zα : Q(Zero ℚ) := q(inferInstance) @@ -695,8 +707,8 @@ meta def evalRatNum : PositivityExt where eval {u α} _ pα? e := do /-- The `positivity` extension which identifies expressions of the form `Rat.den a`. -/ @[positivity Rat.den _] -meta def evalRatDen : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | throwError "not PartialOrder ℕ" +meta def evalRatDen : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => throwError "not PartialOrder ℕ" | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Rat.den $a) => assumeInstancesCommute @@ -705,8 +717,8 @@ meta def evalRatDen : PositivityExt where eval {u α} _ pα? e := do /-- Extension for `posPart`. `a⁺` is always nonnegative, and positive if `a` is. -/ @[positivity _⁺] -meta def evalPosPart : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalPosPart : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match e with | ~q(@posPart _ $instαpospart $a) => let _instαlat ← synthInstanceQ q(Lattice $α) @@ -723,8 +735,8 @@ meta def evalPosPart : PositivityExt where eval {u α} zα pα? e := do /-- Extension for `negPart`. `a⁻` is always nonnegative. -/ @[positivity _⁻] -meta def evalNegPart : PositivityExt where eval {u α} _ pα? e := do - let some _ := pα? | pure .none +meta def evalNegPart : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match e with | ~q(@negPart _ $instαnegpart $a) => let _instαlat ← synthInstanceQ q(Lattice $α) @@ -735,8 +747,8 @@ meta def evalNegPart : PositivityExt where eval {u α} _ pα? e := do /-- Extension for the `positivity` tactic: nonnegative maps take nonnegative values. -/ @[positivity DFunLike.coe _ _] -meta def evalMap : PositivityExt where eval {_ β} _ pβ? e := do - let some _ := pβ? | pure .none +meta def evalMap : PositivityExt where eval {_ β} _ pβ? e := + match pβ? with | none => pure .none | some _ => do let .app (.app _ f) a ← whnfR e | throwError "not ↑f · where f is of NonnegHomClass" let pa ← mkAppOptM ``apply_nonneg #[none, none, β, none, none, none, none, f, a] diff --git a/Mathlib/Tactic/Positivity/Core.lean b/Mathlib/Tactic/Positivity/Core.lean index 45a3faaa15fde1..b69cd0624625f5 100644 --- a/Mathlib/Tactic/Positivity/Core.lean +++ b/Mathlib/Tactic/Positivity/Core.lean @@ -417,17 +417,17 @@ It assumes `t₁` has already been run for a result, and runs `t₂` and takes t It will skip `t₂` if `t₁` is already a proof of `.positive`, and can also combine `.nonnegative` and `.nonzero` to produce a `.positive` result. -/ def orElse {pα?} {e : Q($α)} (t₁ : Strictness zα e pα?) (t₂ : MetaM (Strictness zα e pα?)) : - MetaM (Strictness zα e pα?) := do + MetaM (Strictness zα e pα?) := match t₁ with | .none => catchNone t₂ | p@(.positive _) => pure p - | .nonnegative p₁ => + | .nonnegative p₁ => do match ← catchNone t₂ with | p@(.positive _) => pure p | .nonzero p₂ => pure (.positive q(lt_of_le_of_ne' $p₁ $p₂)) | _ => pure (.nonnegative p₁) - | .nonzero p₁ => - match ← catchNone t₂ with + | .nonzero p₁ => do + match (dependent := true) ← catchNone t₂ with | p@(.positive _) => pure p | .nonnegative p₂ => pure (.positive q(lt_of_le_of_ne' $p₂ $p₁)) | _ => pure (.nonzero p₁) @@ -442,22 +442,22 @@ def core (pα? : Option Q(PartialOrder $α)) (e : Q($α)) : MetaM (Strictness z catch err => trace[Tactic.positivity] "{e} failed: {err.toMessageData}" trace[Tactic.positivity] "current result from positivity extensions: {result.toString}" - match pα? with - | some pα => + match h : pα?, result with + | some pα, res => trace[Tactic.positivity] "{α} has some {pα}" - result ← orElse result <| normNumPositivity zα pα e - trace[Tactic.positivity] "current result from normNum: {result.toString}" - result ← orElse result <| positivityCanon zα pα e - trace[Tactic.positivity] "current result from canonicity: {result.toString}" - if let .positive _ := result then - trace[Tactic.positivity] "{e} => {result.toString}" - return result + let mut res ← orElse res <| normNumPositivity zα pα e + trace[Tactic.positivity] "current result from normNum: {res.toString}" + res ← orElse res <| positivityCanon zα pα e + trace[Tactic.positivity] "current result from canonicity: {res.toString}" + if let .positive _ := res then + trace[Tactic.positivity] "{e} => {res.toString}" + return h ▸ res for ldecl in ← getLCtx do if !ldecl.isImplementationDetail then - result ← orElse result <| compareHyp zα pα e ldecl - trace[Tactic.positivity] "{e} => {result.toString}" - throwNone (pure result) - | .none => + res ← orElse res <| compareHyp zα pα e ldecl + trace[Tactic.positivity] "{e} => {res.toString}" + throwNone (pure (h ▸ res)) + | .none, _ => trace[Tactic.positivity] "{α} has no PartialOrder" if let .nonzero _ := result then trace[Tactic.positivity] "{e} => {result.toString}" @@ -486,10 +486,10 @@ def bestResult (e : Expr) : MetaM (Bool × Expr) := do let zα ← synthInstanceQ q(Zero $α) let pα? ← try? <| synthInstanceQ q(PartialOrder $α) assumeInstancesCommute - match ← try? (Meta.Positivity.core zα pα? e) with - | some (.positive pf) => pure (true, pf) - | some (.nonnegative pf) => pure (false, pf) - | _ => throwError "could not establish the nonnegativity of {e}" + match pα?, ← try? (Meta.Positivity.core zα pα? e) with + | _, some (.positive pf) => pure (true, pf) + | _, some (.nonnegative pf) => pure (false, pf) + | _, _ => throwError "could not establish the nonnegativity of {e}" /-- Given an expression `e`, use the core method of the `positivity` tactic to prove it nonnegative. -/ @@ -508,7 +508,8 @@ def solve (t : Q(Prop)) : MetaM Expr := do let r ← catchNone <| Meta.Positivity.core zα pα? e let throw (a b : String) : MetaM Expr := throwError "failed to prove {a}, but it would be possible to prove {b} if desired" - if let some _ := pα? then + match (dependent := true) pα? with + | some _ => match relDesired, r with | .lt, .positive p | .le, .nonnegative p @@ -523,7 +524,7 @@ def solve (t : Q(Prop)) : MetaM Expr := do | .ne, .nonnegative _ | .ne', .nonnegative _ => throw "nonzeroness" "nonnegativity" | _, .none => throwError "failed to prove positivity/nonnegativity/nonzeroness" - else + | none => match relDesired, r with | .ne, .nonzero p => pure p | .ne', .nonzero p => pure q(Ne.symm $p) diff --git a/Mathlib/Tactic/Positivity/Finset.lean b/Mathlib/Tactic/Positivity/Finset.lean index 50fb47d5c610e6..139070a33db6d9 100644 --- a/Mathlib/Tactic/Positivity/Finset.lean +++ b/Mathlib/Tactic/Positivity/Finset.lean @@ -27,10 +27,10 @@ open Qq Lean Meta Finset It calls `Mathlib.Meta.proveFinsetNonempty` to attempt proving that the finset is nonempty. -/ @[positivity Finset.card _] -meta def evalFinsetCard : PositivityExt where eval {u α} _ pα? e := do +meta def evalFinsetCard : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(Finset.card $s) => - let some _ := pα? | pure .none let some ps ← proveFinsetNonempty s | return .none assertInstancesCommute return .positive q(Finset.Nonempty.card_pos $ps) @@ -38,10 +38,10 @@ meta def evalFinsetCard : PositivityExt where eval {u α} _ pα? e := do /-- Extension for `Fintype.card`. `Fintype.card α` is positive if `α` is nonempty. -/ @[positivity Fintype.card _] -meta def evalFintypeCard : PositivityExt where eval {u α} _ pα? e := do +meta def evalFintypeCard : PositivityExt where eval {u α} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℕ), ~q(@Fintype.card $β $instβ) => - let some _ := pα? | pure .none let instβno ← synthInstanceQ q(Nonempty $β) assumeInstancesCommute return .positive q(@Fintype.card_pos $β $instβ $instβno) @@ -51,10 +51,10 @@ meta def evalFintypeCard : PositivityExt where eval {u α} _ pα? e := do It calls `Mathlib.Meta.proveFinsetNonempty` to attempt proving that the finset is nonempty. -/ @[positivity Finset.dens _] -meta def evalFinsetDens : PositivityExt where eval {u 𝕜} _ pα? e := do +meta def evalFinsetDens : PositivityExt where eval {u 𝕜} _ pα? e := + match pα? with | none => pure .none | some _ => do match u, 𝕜, e with | 0, ~q(ℚ≥0), ~q(@Finset.dens $α $instα $s) => - let some _ := pα? | pure .none let some ps ← proveFinsetNonempty s | return .none assumeInstancesCommute return .positive q(@Nonempty.dens_pos $α $instα $s $ps) @@ -71,21 +71,23 @@ example (s : Finset ℕ) (f : ℕ → ℤ) (hf : ∀ n, 0 ≤ f n) : 0 ≤ s.sum because `compareHyp` can't look for assumptions behind binders. -/ @[positivity Finset.sum _ _] -meta def evalFinsetSum : PositivityExt where eval {u α} zα pα? e := do +meta def evalFinsetSum : PositivityExt where eval {u α} zα pα? e := + match pα? with + | none => pure .none -- TODO: the case without PartialOrder + | some pα => do match e with | ~q(@Finset.sum $ι _ $instα $s $f) => let i : Q($ι) ← mkFreshExprMVarQ q($ι) .syntheticOpaque have body : Q($α) := .betaRev f #[i] - let rbody ← core zα pα? body - let some pα := pα? | pure .none -- TODO: the case without PartialOrder - let p_pos : Option Q(0 < $e) := ← (do + let rbody ← core zα pα body + let p_pos : Option Q(0 < $e) ← do let .positive pbody := rbody | pure none -- Fail if the body is not provably positive let some ps ← proveFinsetNonempty s | pure none let .some pα' ← trySynthInstanceQ q(IsOrderedCancelAddMonoid $α) | pure none assertInstancesCommute let pr : Q(∀ i, 0 < $f i) ← mkLambdaFVars #[i] pbody - return some q(@sum_pos $ι $α $instα (@PartialOrder.toPreorder _ $pα) $pα' $f $s _ - (fun i _ ↦ $pr i) $ps)) + pure <| some q(@sum_pos $ι $α $instα (@PartialOrder.toPreorder _ $pα) $pα' $f $s _ + (fun i _ ↦ $pr i) $ps) -- Try to show that the sum is positive if let some p_pos := p_pos then return .positive p_pos diff --git a/Mathlib/Tactic/Push.lean b/Mathlib/Tactic/Push.lean index 4a40d1ee13b244..bffb4f1db6b571 100644 --- a/Mathlib/Tactic/Push.lean +++ b/Mathlib/Tactic/Push.lean @@ -127,8 +127,12 @@ def pushCore (head : Head) (cfg : Config) (disch? : Option Simp.Discharge) (tgt (simpTheorems := #[]) (congrTheorems := ← getSimpCongrTheorems) let methods := match disch? with - | none => { pre := pushStep head cfg } - | some disch => { pre := pushStep head cfg, discharge? := disch, wellBehavedDischarge := false } + | none => { pre := pushStep head cfg, post _ := return .continue } + | some disch => { + pre := pushStep head cfg, + post _ := return .continue, + discharge? := disch, + wellBehavedDischarge := false } (·.1) <$> Simp.main tgt ctx (methods := methods) /-- Try to rewrite using a `pull` lemma. -/ diff --git a/Mathlib/Tactic/ReduceModChar.lean b/Mathlib/Tactic/ReduceModChar.lean index 11702f4f5e7b11..115eed60892f84 100644 --- a/Mathlib/Tactic/ReduceModChar.lean +++ b/Mathlib/Tactic/ReduceModChar.lean @@ -258,9 +258,10 @@ partial def derive (expensive := false) (e : Expr) : MetaM Simp.Result := do (simpTheorems := #[← ext.getTheorems]) let discharge := Mathlib.Meta.NormNum.discharge let r : Simp.Result := {expr := e} - let pre := Simp.preDefault #[] >> fun e => + let matchAndNorm : Simproc := fun e => try return (Simp.Step.done (← matchAndNorm (expensive := expensive) e)) catch _ => pure .continue + let pre := Simp.preDefault #[] >> matchAndNorm let post := Simp.postDefault #[] let r ← r.mkEqTrans (← Simp.main r.expr ctx (methods := { pre, post, discharge? := discharge })).1 diff --git a/Mathlib/Tactic/Rename.lean b/Mathlib/Tactic/Rename.lean index 8fed33ca313a27..638721ead3adcc 100644 --- a/Mathlib/Tactic/Rename.lean +++ b/Mathlib/Tactic/Rename.lean @@ -10,6 +10,7 @@ public import Mathlib.Init /-! # The `rename'` tactic + The `rename'` tactic renames one or several hypotheses. -/ diff --git a/Mathlib/Tactic/Ring/Basic.lean b/Mathlib/Tactic/Ring/Basic.lean index 6875ffafe604ec..ea4a8ac1fb1c2c 100644 --- a/Mathlib/Tactic/Ring/Basic.lean +++ b/Mathlib/Tactic/Ring/Basic.lean @@ -231,7 +231,7 @@ partial def ExProd.evalNatCast {a : Q(ℕ)} (va : ExProd sβ a) : AtomM (Result -/ partial def ExSum.evalNatCast {a : Q(ℕ)} (va : ExSum sβ a) : AtomM (Result (ExSum sα) q($a)) := do assumeInstancesCommute - match va with + match (dependent := true) va with | .zero => pure ⟨_, .zero, q(natCast_zero (R := $α))⟩ | .add va₁ va₂ => do let ⟨_, vb₁, pb₁⟩ ← ExProd.evalNatCast va₁ @@ -378,14 +378,13 @@ partial def add {u : Lean.Level} {α : Q(Type u)} (sα : Q(CommSemiring $α)) {a b : Q($α)} (za : RatCoeff a) (zb : RatCoeff b) : MetaM (Result RatCoeff q($a + $b) × Option Q(IsNat ($a + $b) 0)) := do let res ← za.toResult.add zb.toResult - let isZero : MetaM (Option Q(IsNat ($a + $b) 0)) ← match res with - | Result.isNat inst lit pf => do - if lit.natLit! == 0 then - have : $lit =Q 0 := ⟨⟩ - pure <| some q($pf) - else - pure none - | _ => pure none + let isZero ← match res with + | Result.isNat _inst lit pf => + if lit.natLit! == 0 then + pure <| some (pf : Q(IsNat ($a + $b) 0)) + else + pure none + | _ => pure none let r ← RatCoeff.ofResult res return ⟨r, isZero⟩ diff --git a/Mathlib/Tactic/Ring/Common.lean b/Mathlib/Tactic/Ring/Common.lean index bba23ff5496407..868611fdfd5cc9 100644 --- a/Mathlib/Tactic/Ring/Common.lean +++ b/Mathlib/Tactic/Ring/Common.lean @@ -525,7 +525,7 @@ and `xy + -xy = 0` is a `.zero` overlap. def evalAddOverlap {a b : Q($α)} (va : ExProd bt sα a) (vb : ExProd bt sα b) : OptionT MetaM (Overlap bt sα q($a + $b)) := do Lean.Core.checkSystem decl_name%.toString - match va, vb with + match (dependent := true) va, vb with | .const za, .const zb => do let ⟨⟨_, zc, pf⟩, isZero⟩ ← rc.add za zb match isZero with @@ -568,12 +568,12 @@ theorem add_pf_add_gt (b₁ : R) (_ : a + b₂ = c) : a + (b₁ + b₂) = b₁ + * `(a₁ + a₂) + (b₁ + b₂) = b₁ + ((a₁ + a₂) + b₂)` (if not `a₁.lt b₁`) -/ partial def evalAdd {a b : Q($α)} (va : ExSum bt sα a) (vb : ExSum bt sα b) : - MetaM <| Result (ExSum bt sα) q($a + $b) := do - Lean.Core.checkSystem decl_name%.toString + MetaM <| Result (ExSum bt sα) q($a + $b) := + Lean.Core.checkSystem decl_name%.toString *> match va, vb with - | .zero, vb => return ⟨b, vb, q(add_pf_zero_add $b)⟩ - | va, .zero => return ⟨a, va, q(add_pf_add_zero $a)⟩ - | .add (a := a₁) (b := _a₂) va₁ va₂, .add (a := b₁) (b := _b₂) vb₁ vb₂ => + | .zero, vb => do return ⟨b, vb, q(add_pf_zero_add $b)⟩ + | va, .zero => do return ⟨a, va, q(add_pf_add_zero $a)⟩ + | .add (a := a₁) (b := _a₂) va₁ va₂, .add (a := b₁) (b := _b₂) vb₁ vb₂ => do have va := .add va₁ va₂; have vb := .add vb₁ vb₂ -- FIXME: why does `va@(...)` fail? match ← (evalAddOverlap rc rcℕ va₁ vb₁).run with | some (.nonzero ⟨_, vc₁, pc₁⟩) => @@ -619,20 +619,20 @@ theorem mul_pp_pf_overlap {ea eb e : ℕ} (x : R) (_ : ea + eb = e) (_ : a₂ * * `(a₁ * a₂) * (b₁ * b₂) = b₁ * ((a₁ * a₂) * b₂)` (if not `a₁.lt b₁`) -/ partial def evalMulProd {a b : Q($α)} (va : ExProd bt sα a) (vb : ExProd bt sα b) : - MetaM <| Result (ExProd bt sα) q($a * $b) := do - Lean.Core.checkSystem decl_name%.toString + MetaM <| Result (ExProd bt sα) q($a * $b) := + Lean.Core.checkSystem decl_name%.toString *> match va, vb with - | .const za, .const zb => + | .const za, .const zb => do let ⟨_, zc, pf⟩ ← rc.mul za zb assumeInstancesCommute return ⟨_, .const zc, q($pf)⟩ - | .mul (x := a₁) (e := a₂) va₁ va₂ va₃, vb@(.const _) => + | .mul (x := a₁) (e := a₂) va₁ va₂ va₃, vb@(.const _) => do let ⟨_, vc, pc⟩ ← evalMulProd va₃ vb return ⟨_, .mul va₁ va₂ vc, q(mul_pf_left $a₁ $a₂ $pc)⟩ - | va@(.const _), .mul (x := b₁) (e := b₂) vb₁ vb₂ vb₃ => + | va@(.const _), .mul (x := b₁) (e := b₂) vb₁ vb₂ vb₃ => do let ⟨_, vc, pc⟩ ← evalMulProd va vb₃ return ⟨_, .mul vb₁ vb₂ vc, q(mul_pf_right $b₁ $b₂ $pc)⟩ - | .mul (x := xa) (e := ea) vxa vea va₂, .mul (x := xb) (e := eb) vxb veb vb₂ => + | .mul (x := xa) (e := ea) vxa vea va₂, .mul (x := xb) (e := eb) vxb veb vb₂ => do have va := .mul vxa vea va₂; have vb := .mul vxb veb vb₂ -- FIXME: why does `va@(...)` fail? let ⟨ea', vea'⟩ := vea.toExProd let ⟨eb', veb'⟩ := veb.toExProd @@ -664,10 +664,10 @@ theorem mul_add {d : R} (_ : (a : R) * b₁ = c₁) (_ : a * b₂ = c₂) (_ : c * `a * (b₁ + b₂) = (a * b₁) + (a * b₂)` -/ def evalMul₁ {a b : Q($α)} (va : ExProd bt sα a) (vb : ExSum bt sα b) : - MetaM <| Result (ExSum bt sα) q($a * $b) := do + MetaM <| Result (ExSum bt sα) q($a * $b) := match vb with - | .zero => return ⟨_, .zero, q(mul_zero $a)⟩ - | .add vb₁ vb₂ => + | .zero => do return ⟨_, .zero, q(mul_zero $a)⟩ + | .add vb₁ vb₂ => do let ⟨_, vc₁, pc₁⟩ ← evalMulProd rc rcℕ va vb₁ let ⟨_, vc₂, pc₂⟩ ← evalMul₁ va vb₂ let ⟨_, vd, pd⟩ ← evalAdd rc rcℕ vc₁.toSum vc₂ @@ -684,10 +684,10 @@ theorem add_mul {d : R} (_ : (a₁ : R) * b = c₁) (_ : a₂ * b = c₂) (_ : c * `(a₁ + a₂) * b = (a₁ * b) + (a₂ * b)` -/ def evalMul {a b : Q($α)} (va : ExSum bt sα a) (vb : ExSum bt sα b) : - MetaM <| Result (ExSum bt sα) q($a * $b) := do + MetaM <| Result (ExSum bt sα) q($a * $b) := match va with - | .zero => return ⟨_, .zero, q(zero_mul $b)⟩ - | .add va₁ va₂ => + | .zero => do return ⟨_, .zero, q(zero_mul $b)⟩ + | .add va₁ va₂ => do let ⟨_, vc₁, pc₁⟩ ← evalMul₁ rc rcℕ va₁ vb let ⟨_, vc₂, pc₂⟩ ← evalMul va₂ vb let ⟨_, vd, pd⟩ ← evalAdd rc rcℕ vc₁ vc₂ @@ -707,13 +707,13 @@ theorem neg_mul {R} [CommRing R] (a₁ : R) (a₂) {a₃ b : R} * `-(a₁ * a₂) = a₁ * -a₂` -/ def evalNegProd {a : Q($α)} (rα : Q(CommRing $α)) (va : ExProd bt sα a) : - MetaM <| Result (ExProd bt sα) q(-$a) := do - Lean.Core.checkSystem decl_name%.toString + MetaM <| Result (ExProd bt sα) q(-$a) := + Lean.Core.checkSystem decl_name%.toString *> match va with - | .const za => + | .const za => do let ⟨b, zb, pb⟩ ← rc.neg q($rα) za return ⟨b, .const zb, q($pb)⟩ - | .mul (x := a₁) (e := a₂) va₁ va₂ va₃ => + | .mul (x := a₁) (e := a₂) va₁ va₂ va₃ => do let ⟨_, vb, pb⟩ ← evalNegProd rα va₃ assumeInstancesCommute return ⟨_, .mul va₁ va₂ vb, q(neg_mul $a₁ $a₂ $pb)⟩ @@ -730,11 +730,13 @@ theorem neg_add {R} [CommRing R] {a₁ a₂ b₁ b₂ : R} * `-(a₁ + a₂) = -a₁ + -a₂` -/ def evalNeg {a : Q($α)} (rα : Q(CommRing $α)) (va : ExSum bt sα a) : - MetaM <| Result (ExSum bt sα) q(-$a) := do - assumeInstancesCommute + MetaM <| Result (ExSum bt sα) q(-$a) := match va with - | .zero => return ⟨_, .zero, q(neg_zero (R := $α))⟩ - | .add va₁ va₂ => + | .zero => do + assumeInstancesCommute + return ⟨_, .zero, q(neg_zero (R := $α))⟩ + | .add va₁ va₂ => do + assumeInstancesCommute let ⟨_, vb₁, pb₁⟩ ← evalNegProd rc rα va₁ let ⟨_, vb₂, pb₂⟩ ← evalNeg rα va₂ return ⟨_, .add vb₁ vb₂, q(neg_add $pb₁ $pb₂)⟩ @@ -913,9 +915,9 @@ In all other cases we use `evalPowProdAtom`. def evalPowProd {a : Q($α)} {b : Q(ℕ)} (va : ExProd bt sα a) (vb : ExProdNat b) : MetaM <| Result (ExProd bt sα) q($a ^ $b) := do Lean.Core.checkSystem decl_name%.toString - let res : OptionT MetaM (Result (ExProd bt sα) q($a ^ $b)) := do + let res : OptionT MetaM (Result (ExProd bt sα) q($a ^ $b)) := match va with - | va@(.const za) => + | va@(.const za) => do match rc.isOne za with | .some pf => return ⟨_, va, q(one_pow $b $pf)⟩ @@ -923,7 +925,7 @@ def evalPowProd {a : Q($α)} {b : Q(ℕ)} (va : ExProd bt sα a) (vb : ExProdNat -- NOTE: rc.pow may fail, e.g. for `ring` when `vb` is not a constant. let ⟨_, zc, pc⟩ ← rc.pow za vb return ⟨_, .const zc, q($pc)⟩ - | .mul vxa₁ (e := ea₁) vea₁ va₂ => + | .mul vxa₁ (e := ea₁) vea₁ va₂ => do let ⟨ea₁', vea₁'⟩ := vea₁.toExProd let ⟨b', vb'⟩ := vb.toExProd let ⟨c₁, vc₁, pc₁⟩ ← evalMulProd rcℕ rcℕ vea₁' vb' @@ -1002,15 +1004,15 @@ Otherwise `a ^ b` is just encoded as `a ^ b * 1 + 0` using `evalPowAtom`. -/ partial def evalPow₁ {a : Q($α)} {b : Q(ℕ)} (va : ExSum bt sα a) (vb : ExProdNat b) : MetaM <| Result (ExSum bt sα) q($a ^ $b) := do - let notPowOne : MetaM <| Result (ExSum bt sα) q($a ^ $b) := do + let notPowOne : MetaM <| Result (ExSum bt sα) q($a ^ $b) := match va with - | .zero => match vb.evalPos with + | .zero => do match vb.evalPos with | some p => return ⟨_, .zero, q(zero_pow (R := $α) $p)⟩ | none => return evalPowAtom rc (.sum .zero) vb - | ExSum.add va .zero => -- TODO: using `.add` here takes a while to compile? + | ExSum.add va .zero => do -- TODO: using `.add` here takes a while to compile? let ⟨_, vc, pc⟩ ← evalPowProd rc rcℕ va vb return ⟨_, vc.toSum, q(single_pow $pc)⟩ - | va => + | va => do -- FIXME: condition used to be k.coeff > 1. Should go back to something like this. let ⟨k, _, vc, pc⟩ := extractCoeff rcℕ vb if k.natLit! > 1 then @@ -1020,7 +1022,7 @@ partial def evalPow₁ {a : Q($α)} {b : Q(ℕ)} (va : ExSum bt sα a) (vb : ExP else return evalPowAtom rc (.sum va) vb match vb with - | .const zb => + | .const zb => do match rcℕ.isOne zb with | .some pf => assumeInstancesCommute @@ -1042,13 +1044,13 @@ theorem pow_add {b₁ b₂ : ℕ} {d : R} * `a ^ (b₁ + b₂) = a ^ b₁ * a ^ b₂` -/ def evalPow {a : Q($α)} {b : Q(ℕ)} (va : ExSum bt sα a) (vb : ExSumNat b) : - MetaM <| Result (ExSum bt sα) q($a ^ $b) := do + MetaM <| Result (ExSum bt sα) q($a ^ $b) := match vb with - | .zero => + | .zero => do let ⟨_, one, pf⟩ := rc.one assumeInstancesCommute return ⟨_, (ExProd.const (one)).toSum, q(pow_zero $a $pf)⟩ - | .add vb₁ vb₂ => + | .add vb₁ vb₂ => do let ⟨_, vc₁, pc₁⟩ ← evalPow₁ rc rcℕ va vb₁ let ⟨_, vc₂, pc₂⟩ ← evalPow va vb₂ let ⟨_, vd, pd⟩ ← evalMul rc rcℕ vc₁ vc₂ @@ -1130,10 +1132,10 @@ def evalInvAtom (a : Q($α)) : AtomM (Result (ExBase bt sα) q($a⁻¹)) := do * `(a ^ b * c)⁻¹ = a⁻¹ ^ b * c⁻¹` -/ def ExProd.evalInv {a : Q($α)} (czα : Option Q(CharZero $α)) (va : ExProd bt sα a) : - AtomM (Result (ExProd bt sα) q($a⁻¹)) := do - Lean.Core.checkSystem decl_name%.toString + AtomM (Result (ExProd bt sα) q($a⁻¹)) := + Lean.Core.checkSystem decl_name%.toString *> match va with - | .const c => + | .const c => do match ← rc.inv czα q($dsα) c with | some ⟨_, vd, pd⟩ => pure ⟨_, .const vd, q($pd)⟩ | none => diff --git a/Mathlib/Tactic/Ring/Compare.lean b/Mathlib/Tactic/Ring/Compare.lean index 08c15416b30deb..7d3f9da5a05e6a 100644 --- a/Mathlib/Tactic/Ring/Compare.lean +++ b/Mathlib/Tactic/Ring/Compare.lean @@ -134,8 +134,8 @@ def evalLE {v : Level} {α : Q(Type v)} let ⟨_, pz⟩ ← NormNum.mkOfNat α q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0) let rz : NormNum.Result q((0:$α)) := NormNum.Result.isNat q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0) - (q(NormNum.isNat_ofNat $α $pz):) - match va, vb with + (q(NormNum.isNat_ofNat $α $pz):) + match (dependent := true) va, vb with /- `0 ≤ 0` -/ | .zero, .zero => pure <| .ok (q(le_refl (0:$α)):) /- For numerals `ca` and `cb`, `ca + x ≤ cb + x` if `ca ≤ cb` -/ @@ -173,8 +173,8 @@ def evalLT {v : Level} {α : Q(Type v)} let ⟨_, pz⟩ ← NormNum.mkOfNat α q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0) let rz : NormNum.Result q((0:$α)) := NormNum.Result.isNat q(addMonoidWithOneOfCommSemiring $α) q(nat_lit 0) - (q(NormNum.isNat_ofNat $α $pz):) - match va, vb with + (q(NormNum.isNat_ofNat $α $pz):) + match (dependent := true) va, vb with /- `0 < 0` -/ | .zero, .zero => return .error tooSmall /- For numerals `ca` and `cb`, `ca + x < cb + x` if `ca < cb` -/ diff --git a/Mathlib/Tactic/Simproc/ExistsAndEq.lean b/Mathlib/Tactic/Simproc/ExistsAndEq.lean index bd82a4b79ea7c0..0684540ca62d4d 100644 --- a/Mathlib/Tactic/Simproc/ExistsAndEq.lean +++ b/Mathlib/Tactic/Simproc/ExistsAndEq.lean @@ -127,7 +127,7 @@ where assertUnreachable "findEq: some side of equality must be `a`, and the other must not depend on `a`" | ~q($L ∧ $R) => - match (generalizing := false) path with + match path with | [] => assertUnreachable "findEq: P is conjunction but path is empty" | .left :: tl => let (fvars, lctx, P', a') ← go a q($L) tl @@ -212,17 +212,17 @@ where MetaM Q($goal) := do match goal with | ~q(@Exists $β $pb) => - match (generalizing := false) exs with + match exs with | [] => assertUnreachable "mkAfterToBefore: goal is `Exists` but `exs` is empty" | ⟨v, γ, c⟩ :: exsTail => let _ : u_1 =QL v := ⟨⟩ let _ : $γ =Q $β := ⟨⟩ - let pf1 : Q($pb $c) := ← go h exsTail path + let pf1 : Q($pb $c) ← go h exsTail path return q(Exists.intro $c $pf1) | ~q(And $L $R) => let ~q($L' ∧ $R') := P | assertUnreachable "mkAfterToBefore: goal is `And` but `P` is not `And`" - match (generalizing := false) path with + match path with | [] => assertUnreachable "mkAfterToBefore: goal is `And` but `exs` is empty" | .left :: tl => let _ : $R =Q $R' := ⟨⟩ @@ -250,7 +250,7 @@ partial def withExistsElimAlongPathImp {u : Level} {α : Q(Sort u)} MetaM Q($goal) := do match P with | ~q(@Exists $β $pb) => - match (generalizing := false) exs with + match exs with | [] => assertUnreachable "withExistsElimAlongPathImp: `P` is `Exists` but `exs` is empty" | ⟨v, γ, b⟩ :: exsTail => let _ : u_1 =QL v := ⟨⟩ @@ -261,7 +261,7 @@ partial def withExistsElimAlongPathImp {u : Level} {α : Q(Sort u)} let pf2 : Q(∀ b, $pb b → $goal) ← mkLambdaFVars #[b, hb] pf1 return q(Exists.elim $h $pf2) | ~q(And $L' $R') => - match (generalizing := false) path with + match path with | [] => assertUnreachable "withExistsElimAlongPathImp: `P` is `And` but `path` is empty" | .left :: tl => withExistsElimAlongPathImp q(And.left $h) exs tl hs act @@ -372,21 +372,21 @@ where MetaM Q($goal) := do match P with | ~q(@Exists $β $pb) => - match (generalizing := false) exs with + match exs with | [] => assertUnreachable "mkBeforeToAfter: `P` is `Exists` but `exs` is empty" | ⟨v, γ, b⟩ :: exsTail => let _ : u_1 =QL v := ⟨⟩ let _ : $γ =Q $β := ⟨⟩ - match (generalizing := false) hs with + match hs with | [] => assertUnreachable "mkBeforeToAfter: `P` is `Exists` but `hs` is empty" | ⟨H, hb⟩ :: hsTail => let _ : $H =Q $pb $b := ⟨⟩ - let pf : Q($goal) := ← go hb exsTail hsTail path h_eq + let pf : Q($goal) ← go hb exsTail hsTail path h_eq return pf | ~q(And $L $R) => let ~q($L' ∧ $R') := goal | assertUnreachable "mkBeforeToAfter: `P` is `And` but `goal` is not `And`" - match (generalizing := false) path with + match path with | [] => assertUnreachable "mkBeforeToAfter: `P` is `And` but `path` is empty" | .left :: tl => let pa : Q($α → Prop) ← mkLambdaFVars #[a] R diff --git a/Mathlib/Tactic/TacticAnalysis/Declarations.lean b/Mathlib/Tactic/TacticAnalysis/Declarations.lean index b4dc95d07bb6da..16e41e534055f6 100644 --- a/Mathlib/Tactic/TacticAnalysis/Declarations.lean +++ b/Mathlib/Tactic/TacticAnalysis/Declarations.lean @@ -412,9 +412,9 @@ def Mathlib.TacticAnalysis.tryAtEachStepCore -- Extract just the tactic name, ignoring trailing comments/whitespace -- Use try/catch because ppTactic can fail on certain syntax (e.g., `congr($h x)`) let oldTacticPP := (← try - return ((← liftCoreM <| PrettyPrinter.ppTactic ⟨i.tacI.stx⟩).pretty.splitOn "\n")[0]!.trimAscii + pure (((← liftCoreM <| PrettyPrinter.ppTactic ⟨i.tacI.stx⟩).pretty.splitOn "\n")[0]!.trimAscii) catch _ => - return i.tacI.stx.reprint.getD "???") + pure (i.tacI.stx.reprint.getD "???")) let newTacticPP ← label.getDM (try return ((← liftCoreM <| PrettyPrinter.ppTactic tac).pretty.splitOn "\n")[0]!.trimAscii.copy catch _ => diff --git a/Mathlib/Tactic/ToDual.lean b/Mathlib/Tactic/ToDual.lean index 188b513bd39c32..b0b0650974b835 100644 --- a/Mathlib/Tactic/ToDual.lean +++ b/Mathlib/Tactic/ToDual.lean @@ -24,7 +24,6 @@ to_dual_insert_cast_fun DecidableLT := fun inst a b ↦ inst b a, fun inst a b attribute [to_dual_do_translate] Empty PEmpty Unit PUnit attribute [to_dual_ignore_args 2] Subtype -set_option linter.existingAttributeWarning false in attribute [to_dual self] ge_iff_le gt_iff_lt attribute [to_dual le_of_eq_of_le''] le_of_eq_of_le diff --git a/Mathlib/Tactic/Translate/Core.lean b/Mathlib/Tactic/Translate/Core.lean index 7e2d6eb4249e9e..2688f4e152a0d6 100644 --- a/Mathlib/Tactic/Translate/Core.lean +++ b/Mathlib/Tactic/Translate/Core.lean @@ -40,7 +40,7 @@ In the case of `to_additive`, we may want to apply it multiple times, (such as in `a ^ n` -> `n • a` -> `n +ᵥ a`). In this case, you should use the syntax `to_additive (attr := some_other_attr, to_additive)`, which will apply `some_other_attr` to all three generated declarations. - -/ +-/ syntax attrOption := &"attr" " := " Parser.Term.attrInstance,* syntax reorderOption := &"reorder" " := " translateReorder @@ -615,20 +615,19 @@ def applyReplacementLambda (t : TranslateData) (dontTranslate : List Nat) (e : E /-- Run `applyReplacementFun` on the given `srcDecl` to make a new declaration with name `tgt`. -/ def updateDecl (t : TranslateData) (tgt : Name) (srcDecl : ConstantInfo) - (reorder : Reorder) (dont : List Nat) + (reorder : ArgReorder) (dont : List Nat) (unfoldBoundaries? : Option UnfoldBoundary.UnfoldBoundaries) (rename : NameMap Name) : MetaM (ConstantInfo × Option RelevantArg) := do unless srcDecl.all == [srcDecl.name] do throwError "`{t.attrName}` does not support mutually recursive declarations." let decl := srcDecl.updateName tgt let decl := decl.updateAll [tgt] - let decl := decl.updateLevelParams (reorder.univReorder.permuteList! decl.levelParams) let mut value := decl.value! (allowOpaque := true) if let some b := unfoldBoundaries? then value ← b.cast (← b.insertBoundaries value t.attrName) decl.type t.attrName trace[translate] "Value before translation:{indentExpr value}" let (value', relevantArg₁) ← applyReplacementLambda t dont value - value ← reorderLambda reorder.reorder value' + value ← reorderLambda reorder value' if let some b := unfoldBoundaries? then value ← b.unfoldInsertions value let decl := decl.updateValue value @@ -637,7 +636,7 @@ def updateDecl (t : TranslateData) (tgt : Name) (srcDecl : ConstantInfo) type ← b.insertBoundaries decl.type t.attrName let (type', relevantArg₂) ← applyReplacementForall t dont <| renameBinderNames (t.guessNameExt.getState (← getEnv)) rename type - type ← reorderForall reorder.reorder type' + type ← reorderForall reorder type' if let some b := unfoldBoundaries? then type ← b.unfoldInsertions type return (decl.updateType type, .merge .min relevantArg₁ relevantArg₂) @@ -650,7 +649,7 @@ and only if that fails do we try to include them. The reason is that in the most common case, `to_dual` succeeds without needing to insert unfold boundaries, and figuring out whether to insert them can be quite expensive. -/ def updateAndAddDecl (t : TranslateData) (tgt : Name) (srcDecl : ConstantInfo) - (reorder : Reorder) (dont : List Nat) (rename : NameMap Name) : + (reorder : ArgReorder) (dont : List Nat) (rename : NameMap Name) : MetaM (ConstantInfo × Option RelevantArg) := -- Set `Elab.async` to `false` so that we can catch kernel errors. withOptions (Elab.async.set · false) do @@ -793,7 +792,6 @@ partial def transformDeclRec (t : TranslateData) (cfg : Config) (rootSrc rootTgt let namesPre := (← getConstInfo rootSrc).type.getForallBinderNames let namesSrc := (← getConstInfo src).type.getForallBinderNames pure <| cfg.dontTranslate.filterMap (namesPre[·]? >>= namesSrc.idxOf?) - let reorder := { reorder, univReorder := guessUnivReorder reorder srcDecl } -- now transform the source declaration let (tgtDecl, relevantArg?) ← MetaM.run' <| updateAndAddDecl t tgt srcDecl reorder dontTranslate rename @@ -802,7 +800,7 @@ partial def transformDeclRec (t : TranslateData) (cfg : Config) (rootSrc rootTgt getRelevantArg t cfg relevantArg? src else pure (relevantArg?.getD .noArg) - insertTranslation t src tgt reorder relevantArg cfg.ref + insertTranslation t src tgt { reorder } relevantArg cfg.ref if src == rootSrc && srcDecl.isThm && tgtDecl.type == srcDecl.type then Linter.logLintIf linter.translateRedundant cfg.ref m!"`{t.attrName}` did not change the type \ of theorem `{.ofConstName src}`. Please remove the attribute." @@ -917,7 +915,7 @@ def targetName (t : TranslateData) (cfg : Config) (src : Name) : CoreM Name := d return src if cfg.none then if cfg.target != .anonymous then - logWarning m!"`{t.attrName} private` ignores the provided name {cfg.target}" + logWarning m!"`{t.attrName} none` ignores the provided name {cfg.target}" return ← withDeclNameForAuxNaming src do mkAuxDeclName <| .mkSimple ("_" ++ t.attrName.toString) -- When re-tagging an existing translation, simply return that existing translation. @@ -964,9 +962,9 @@ partial def checkExistingType (t : TranslateData) (src tgt : Name) (cfg : Config MetaM (Reorder × RelevantArg) := withoutExporting do let srcDecl ← getConstInfo src let tgtDecl ← getConstInfo tgt - unless srcDecl.levelParams.length == tgtDecl.levelParams.length do - throwError "`{t.attrName}` validation failed:\n expected {srcDecl.levelParams.length} \ - universe levels, but '{tgt}' has {tgtDecl.levelParams.length} universe levels" + unless srcDecl.numLevelParams == tgtDecl.numLevelParams do + throwError "`{t.attrName}` validation failed:\n expected {srcDecl.numLevelParams} \ + universe levels, but '{tgt}' has {tgtDecl.numLevelParams} universe levels" let mut srcType := srcDecl.type let unfoldBoundaries? ← t.unfoldBoundaries?.mapM (return ·.getState (← getEnv)) if let some b := unfoldBoundaries? then @@ -984,7 +982,6 @@ partial def checkExistingType (t : TranslateData) (src tgt : Name) (cfg : Config pure reorder else pure reorder' - let univReorder := guessUnivReorder reorder srcDecl if cfg.self && reorder.isEmpty then Linter.logLintIf linter.translateRedundant cfg.ref m!"\ `{t.attrName} self` is redundant when none of the arguments are reordered.\n\ @@ -994,12 +991,19 @@ partial def checkExistingType (t : TranslateData) (src tgt : Name) (cfg : Config srcType ← reorderForall reorder srcType if let some b := unfoldBoundaries? then srcType ← b.unfoldInsertions srcType - srcType := srcType.instantiateLevelParams - (univReorder.permuteList! srcDecl.levelParams) (tgtDecl.levelParams.map mkLevelParam) + -- We rely on unification to determine how the universe parameters need to be reordered. + let levels ← mkFreshLevelMVars srcDecl.numLevelParams + srcType := srcType.instantiateLevelParams srcDecl.levelParams levels let tgtType := tgtDecl.type unless ← withReducible <| isDefEq srcType tgtType do throwError "`{t.attrName}` validation failed: expected{indentExpr srcType}\nbut '{tgt}' has \ type{indentExpr tgtType}" + let params ← levels.mapM fun level ↦ do match ← instantiateLevelMVars level with + | .param u => return u + | _ => throwError "inferred universe `{level}` in `{srcType}` is not a parameter." + let some univReorder := getPermutation params.toArray tgtDecl.levelParams.toArray | + throwError "inferred universe parameters {params} \ + are not a reordering of {srcDecl.levelParams}." return ({ univReorder, reorder }, ← getRelevantArg t cfg relevantArg? src) /-- if `f src = #[a_1, ..., a_n]` and `f tgt = #[b_1, ... b_n]` then `proceedFieldsAux src tgt f` @@ -1084,7 +1088,7 @@ def elabTranslationAttr (declName : Name) (stx : Syntax) : CoreM Config := do | `(bracketedOption| (reorder := $reorder)) => if reorder?.isSome then throwErrorAt opt "cannot specify `reorder` multiple times" - reorder? ← elabReorder reorder argNames xs (.ofConstName declName) + reorder? ← some <$> elabReorder reorder argNames xs (.ofConstName declName) | `(bracketedOption| (relevant_arg := $n)) => if relevantArg?.isSome then throwErrorAt opt "cannot specify `relevant_arg` multiple times" @@ -1149,8 +1153,8 @@ partial def applyAttributes (t : TranslateData) (cfg : Config) (src tgt : Name) (relevantArg : RelevantArg) : TermElabM (Array Name) := do -- we only copy the `instance` attribute, since it is nice to directly tag `instance` declarations copyInstanceAttribute src tgt - -- Warn users if the original declaration has an attributee - if !cfg.self && !cfg.none && linter.existingAttributeWarning.get (← getOptions) then + -- Warn users if the original declaration has an attribute + if !cfg.existing && !cfg.none && linter.existingAttributeWarning.get (← getOptions) then let appliedAttrs ← getAllSimpAttrs src if appliedAttrs.size > 0 then let appliedAttrs := ", ".intercalate (appliedAttrs.toList.map toString) diff --git a/Mathlib/Tactic/Translate/Reorder.lean b/Mathlib/Tactic/Translate/Reorder.lean index c2c614edeb632f..6c5e03c1b0a4a8 100644 --- a/Mathlib/Tactic/Translate/Reorder.lean +++ b/Mathlib/Tactic/Translate/Reorder.lean @@ -213,15 +213,13 @@ partial def reorderForall (reorder : ArgReorder) (e : Expr) : MetaM Expr := do /-- Reorder the arguments of a function using the given `ArgReorder`. -/ partial def reorderLambda (reorder : ArgReorder) (e : Expr) : MetaM Expr := do - let (mvars, bis, e) ← lambdaMetaTelescope e reorder.range - let (mvars', bis', _) ← forallMetaBoundedTelescope (← inferType e) (reorder.range - mvars.size) - let mut mvars := mvars ++ mvars' + let (mvars, bis, _) ← forallMetaBoundedTelescope (← inferType e) reorder.range unless mvars.size = reorder.range do throwError "the permutation (reorder := {reorder}) is out of bounds, \ the function{indentExpr e}\nhas only {mvars.size} arguments" - let bis := reorder.permute! (bis ++ bis') |>.toList + let bis := reorder.permute! bis |>.toList -- Note that `mkLambdaFVars` also works with mvars. - fixBinderInfos bis <$> mkLambdaFVars (← reorderMVars mvars reorder) (mkAppN e mvars') + fixBinderInfos bis <$> mkLambdaFVars (← reorderMVars mvars reorder) (e.beta mvars) end @@ -242,38 +240,19 @@ private def decomposePerm {n} (map : Vector (Option (Fin n)) n) : Permutation := map := map.set! j none if j' = i then break j := j' - cycle := ⟨cycle.1 ++ [↑j], by grind⟩ + cycle := ⟨cycle.1 ++ [j.val], by grind⟩ perm := cycle :: perm return perm -/-- Determine the universe level reorder for `decl`, given the argument reorder. -For each reordering in `reorder`, we find any corresponding universe reorderings, -which are then combined to get the result. -/ -def guessUnivReorder (reorder : ArgReorder) (decl : ConstantInfo) : Permutation := Id.run do - let mut map := .replicate decl.levelParams.length none - for ⟨cycle, _⟩ in reorder.perm do - for i in cycle, i' in cycle.tail ++ [cycle.head (by grind)] do - for (u, u') in matchingUnivs (getNthHyp i decl.type) (getNthHyp i' decl.type) do - let some p := getParam? u | pure () - let some p' := getParam? u' | pure () - if p != p' then - let some n := decl.levelParams.finIdxOf? p | pure () - let some n' := decl.levelParams.finIdxOf? p' | pure () - map := map.set n n' - return decomposePerm map -where - getNthHyp : Nat → Expr → Expr - | 0, e => e.bindingDomain! - | n + 1, e => getNthHyp n e.bindingBody! - matchingUnivs (e e' : Expr) : List (Level × Level) := - match e.getAppFn, e'.getAppFn with - | .const n us, .const n' us' => if n == n' then us.zip us' else [] - | .sort u, .sort u' => [(u, u')] - | _, _ => [] - getParam? : Level → Option Name - | .param p => some p - | .succ u => getParam? u - | _ => none +/-- Return the permutation that sends `src` to `tgt`, if it exists. -/ +def getPermutation {α : Type*} [BEq α] (src : Array α) (tgt : Array α) : Option Permutation := do + let n := src.size + if h : n = tgt.size then + have src : Vector α n := src.toVector + have tgt : Vector α n := h ▸ tgt.toVector + return decomposePerm (← src.mapM (some <$> tgt.finIdxOf? ·)) + else + none /-- Determine how many forall binders should be introduced to get a non-dependent conclusion. -/ private def depForallDepth : Expr → Nat diff --git a/Mathlib/Tactic/Translate/ToAdditive.lean b/Mathlib/Tactic/Translate/ToAdditive.lean index 0ce40eceb1fac7..0a5c69fd01a934 100644 --- a/Mathlib/Tactic/Translate/ToAdditive.lean +++ b/Mathlib/Tactic/Translate/ToAdditive.lean @@ -190,8 +190,8 @@ mismatch error. This likely only happens when the multiplicative declaration involves `pow`/`^`. Solutions: * Ensure that the order of arguments of all relevant declarations are the same for the multiplicative and additive version. This might mean that arguments have an "unnatural" order - (e.g. `Monoid.npow n x` corresponds to `x ^ n`, but it is convenient that `Monoid.npow` has this - argument order, since it matches `AddMonoid.nsmul n x`. + (e.g. `NPow.npow n x` corresponds to `x ^ n`, but it is convenient that `NPow.npow` has this + argument order, since it matches `NSMul.nsmul n x`. * If this is not possible, add `(reorder := ...)` argument to `to_additive`. If neither of these solutions work, and `to_additive` is unable to automatically generate the @@ -351,6 +351,7 @@ def abbreviationDict : Std.HashMap String String := .ofList [ ("le_zero", "Nonpos"), ("ltzero", "Neg"), ("lt_zero", "Neg"), + ("addAntidiagonal", "Antidiagonal"), ("addSingle", "Single"), ("addSupport", "Support"), ("addTSupport", "TSupport"), @@ -390,7 +391,9 @@ def abbreviationDict : Std.HashMap String String := .ofList [ ("mapMod", "MapAddMod"), ("modObj", "AddModObj"), ("yonedaMon", "YonedaAddMon"), - ("conGen", "AddConGen")] + ("conGen", "AddConGen"), + ("unoneD", "unzeroD"), + ("unone", "unzero")] @[inherit_doc GuessName.GuessNameExt] initialize guessNameExt : GuessName.GuessNameExt ← diff --git a/Mathlib/Tactic/Variable.lean b/Mathlib/Tactic/Variable.lean index cf6d13901ce531..0988a05e6a9210 100644 --- a/Mathlib/Tactic/Variable.lean +++ b/Mathlib/Tactic/Variable.lean @@ -191,7 +191,7 @@ partial def completeBinders' (maxSteps : Nat) (gas : Nat) trace[«variable?»] m!"elaborated binder types array = {types}" Term.synthesizeSyntheticMVarsNoPostponing -- checkpoint for withAutoBoundImplicit Term.withoutAutoBoundImplicit do - let (binders, toOmit) := ← do + let (binders, toOmit) ← (do match binder with | `(bracketedBinderF|[$[$ident? :]? $ty]) => -- Check if it's an alias @@ -213,7 +213,7 @@ partial def completeBinders' (maxSteps : Nat) (gas : Nat) return (binders, toOmit.push true) else return (binders, toOmit.push false) - | _ => return (binders, toOmit.push false) + | _ => return (binders, toOmit.push false)) completeBinders' maxSteps gas checkRedundant binders toOmit (i + 1) else if h : gas = 0 ∧ i < binders.size then diff --git a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean index 03526eb4ebbb72..4d7a993258824a 100644 --- a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean +++ b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Basic.lean @@ -263,6 +263,8 @@ def ofContinuousMulEquiv {G : ProfiniteGrp.{u}} {H : Type v} [TopologicalSpace H /-- Build an isomorphism in the category `ProfiniteGrp` from a `ContinuousMulEquiv` between `ProfiniteGrp`s. -/ +@[to_additive /-- Build an isomorphism in the category `ProfiniteAddGrp` from +a `ContinuousAddEquiv` between `ProfiniteAddGrp`s. -/] def ContinuousMulEquiv.toProfiniteGrpIso {X Y : ProfiniteGrp} (e : X ≃ₜ* Y) : X ≅ Y where hom := ofHom e inv := ofHom e.symm @@ -279,7 +281,7 @@ instance : (forget₂ ProfiniteGrp Profinite).Faithful := { map_injective := fun {_ _} _ _ h => ConcreteCategory.hom_ext _ _ fun x ↦ CategoryTheory.congr_fun h x } - +@[to_additive] instance : (forget₂ ProfiniteGrp Profinite).ReflectsIsomorphisms where reflects {X Y} f _ := by let i := asIso ((forget₂ ProfiniteGrp Profinite).map f) @@ -288,6 +290,7 @@ instance : (forget₂ ProfiniteGrp Profinite).ReflectsIsomorphisms where map_mul' := map_mul f.hom } exact (ContinuousMulEquiv.toProfiniteGrpIso e).isIso_hom +@[to_additive] instance : (forget ProfiniteGrp.{u}).ReflectsIsomorphisms := CategoryTheory.reflectsIsomorphisms_comp (forget₂ ProfiniteGrp Profinite) (forget Profinite) diff --git a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean index b148f05cb044f6..d56ec3594c15ff 100644 --- a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean +++ b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Completion.lean @@ -56,6 +56,7 @@ namespace ProfiniteCompletion variable (G : GrpCat.{u}) /-- The diagram of finite quotients indexed by finite-index normal subgroups of `G`. -/ +@[to_additive /-- The diagram of finite quotients indexed by finite-index normal subgroups. -/] def finiteGrpDiagram : FiniteIndexNormalSubgroup G ⥤ FiniteGrp.{u} where obj H := FiniteGrp.of <| G ⧸ H.toSubgroup map f := FiniteGrp.ofHom <| QuotientGroup.map _ _ (MonoidHom.id _) f.le @@ -63,16 +64,20 @@ def finiteGrpDiagram : FiniteIndexNormalSubgroup G ⥤ FiniteGrp.{u} where map_comp f g := by ext ⟨x⟩; rfl /-- The finite-quotient diagram viewed in `ProfiniteGrp`. -/ +@[to_additive /-- The finite-quotient diagram viewed in `ProfiniteAddGrp`. -/] def diagram : FiniteIndexNormalSubgroup G ⥤ ProfiniteGrp.{u} := finiteGrpDiagram _ ⋙ forget₂ _ _ /-- The profinite completion of `G` as a projective limit. -/ +@[to_additive /-- The profinite completion of `G` as a projective limit. -/] def completion : ProfiniteGrp.{u} := limit (diagram G) /-- The canonical map from `G` to its profinite completion, as a function. -/ +@[to_additive /-- The canonical map from `G` to its profinite completion, as a function. -/] def etaFn (x : G) : completion G := ⟨fun _ => QuotientGroup.mk x, fun _ _ _ => rfl⟩ /-- The canonical morphism from `G` to its profinite completion. -/ +@[to_additive /-- The canonical morphism from `G` to its profinite completion. -/] def eta : G ⟶ GrpCat.of (completion G) := GrpCat.ofHom { toFun := etaFn G map_one' := rfl @@ -80,6 +85,7 @@ def eta : G ⟶ GrpCat.of (completion G) := GrpCat.ofHom { } set_option backward.isDefEq.respectTransparency false in +@[to_additive] theorem mono_eta_iff_residuallyFinite : Mono (eta G) ↔ Group.ResiduallyFinite G := by rw [GrpCat.mono_iff_injective, injective_iff_map_eq_one, Group.residuallyFinite_iff_forall_finiteIndexNormalSubgroup] @@ -87,11 +93,13 @@ theorem mono_eta_iff_residuallyFinite : Mono (eta G) ↔ Group.ResiduallyFinite rw [Subtype.ext_iff, funext_iff] exact forall_congr' fun H ↦ QuotientGroup.eq_one_iff g +@[to_additive] theorem etaFn_injective_iff_residuallyFinite : Function.Injective (etaFn G) ↔ Group.ResiduallyFinite G := (GrpCat.mono_iff_injective (eta G)).symm.trans (mono_eta_iff_residuallyFinite G) set_option backward.isDefEq.respectTransparency false in +@[to_additive] lemma denseRange : DenseRange (etaFn G) := by apply dense_iff_inter_open.mpr rintro U ⟨s, hsO, hsv⟩ ⟨⟨spc, hspc⟩, uDefaultSpec⟩ @@ -119,14 +127,17 @@ variable {G} variable {P : ProfiniteGrp.{u}} /-- The preimage of an open normal subgroup under a morphism to a profinite group. -/ +@[to_additive /-- The preimage of an open normal subgroup under a morphism to a profinite group. -/] def preimage (f : G ⟶ GrpCat.of P) (H : OpenNormalSubgroup P) : FiniteIndexNormalSubgroup G := H.toFiniteIndexNormalSubgroup.comap f.hom +@[to_additive] lemma preimage_le {f : G ⟶ GrpCat.of P} {H K : OpenNormalSubgroup P} (h : H ≤ K) : preimage f H ≤ preimage f K := FiniteIndexNormalSubgroup.comap_mono _ h /-- The induced map on finite quotients coming from a morphism to `P`. -/ +@[to_additive /-- The induced map on finite quotients coming from a morphism to `P`. -/] def quotientMap (f : G ⟶ GrpCat.of P) (H : OpenNormalSubgroup P) : FiniteGrp.of (G ⧸ (preimage f H).toSubgroup) ⟶ FiniteGrp.of (P ⧸ H.toSubgroup) := FiniteGrp.ofHom <| QuotientGroup.map _ _ f.hom <| fun _ h => h @@ -163,6 +174,7 @@ lemma lift_eta (f : G ⟶ GrpCat.of P) : eta G ≫ (forget₂ _ _).map (lift f) simp only [Category.assoc, Iso.inv_hom_id] rfl +@[to_additive] lemma lift_unique (f g : completion G ⟶ P) (h : eta G ≫ (forget₂ _ _).map f = eta G ≫ (forget₂ _ _).map g) : f = g := by ext x diff --git a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean index 8c314bc61cabf3..e05a25f8247f72 100644 --- a/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean +++ b/Mathlib/Topology/Algebra/Category/ProfiniteGrp/Limits.lean @@ -43,6 +43,8 @@ namespace ProfiniteGrp /-- The functor from `OpenNormalSubgroup P` to `FiniteGrp` sending `U` to `P ⧸ U`, where `P : ProfiniteGrp`. -/ +@[to_additive /-- The functor from `OpenNormalAddSubgroup P` to `FiniteAddGrp` sending `U` to +`P ⧸ U`, where `P : ProfiniteAddGrp`. -/] def toFiniteQuotientFunctor (P : ProfiniteGrp) : OpenNormalSubgroup P ⥤ FiniteGrp where obj := fun H => FiniteGrp.of (P ⧸ H.toSubgroup) map := fun fHK => FiniteGrp.ofHom (QuotientGroup.map _ _ (.id _) (leOfHom fHK)) @@ -51,18 +53,22 @@ def toFiniteQuotientFunctor (P : ProfiniteGrp) : OpenNormalSubgroup P ⥤ Finite _ _ _ (.id _) (.id _) (leOfHom f) (leOfHom g)).symm /-- The diagram of finite quotients of `P` viewed in `ProfiniteGrp`. -/ -@[simps! obj map] +@[to_additive (attr := simps! obj map) +/-- The diagram of finite quotients of `P` viewed in `ProfiniteAddGrp`. -/] def diagram (P : ProfiniteGrp.{u}) : OpenNormalSubgroup P ⥤ ProfiniteGrp.{u} := toFiniteQuotientFunctor P ⋙ forget₂ FiniteGrp ProfiniteGrp /-- The `MonoidHom` from a profinite group `P` to the projective limit of its quotients by open normal subgroups ordered by inclusion -/ +@[to_additive /-- The `AddMonoidHom` from a profinite additive group `P` to the projective limit of +its quotients by open normal subgroups ordered by inclusion -/] def toLimitFun (P : ProfiniteGrp.{u}) : P →* limit (diagram P) where toFun p := ⟨fun _ => QuotientGroup.mk p, fun _ ↦ fun _ _ ↦ rfl⟩ map_one' := Subtype.val_inj.mp rfl map_mul' _ _ := Subtype.val_inj.mp rfl set_option backward.isDefEq.respectTransparency false in +@[to_additive] lemma toLimitFun_continuous (P : ProfiniteGrp.{u}) : Continuous (toLimitFun P) := by apply continuous_induced_rng.mpr (continuous_pi _) intro H @@ -81,6 +87,8 @@ lemma toLimitFun_continuous (P : ProfiniteGrp.{u}) : Continuous (toLimitFun P) : /-- The morphism in the category of `ProfiniteGrp` from a profinite group `P` to the projective limit of its quotients by open normal subgroups ordered by inclusion -/ +@[to_additive /-- The morphism in the category of `ProfiniteAddGrp` from a profinite additive group +`P` to the projective limit of its quotients by open normal subgroups ordered by inclusion -/] def toLimit (P : ProfiniteGrp.{u}) : P ⟶ limit (diagram P) := ofHom { toLimitFun P with continuous_toFun := toLimitFun_continuous P } @@ -115,6 +123,7 @@ theorem toLimit_surjective (P : ProfiniteGrp.{u}) : Function.Surjective (toLimit rw [← Set.range_eq_univ, ← closure_eq_iff_isClosed.mpr this, Dense.closure_eq (denseRange_toLimit P)] +@[to_additive] theorem toLimit_injective (P : ProfiniteGrp.{u}) : Function.Injective (toLimit P) := by change Function.Injective (toLimit P).hom.toMonoidHom rw [← MonoidHom.ker_eq_bot_iff, Subgroup.eq_bot_iff_forall] @@ -144,6 +153,7 @@ noncomputable def isoLimittoFiniteQuotientFunctor (P : ProfiniteGrp.{u}) : ContinuousMulEquiv.toProfiniteGrpIso (continuousMulEquivLimittoFiniteQuotientFunctor P) /-- The projection from `P` to the quotient by an open normal subgroup. -/ +@[to_additive /-- The projection from `P` to the quotient by an open normal subgroup. -/] def proj {P : ProfiniteGrp.{u}} (U : OpenNormalSubgroup P) : P ⟶ (diagram P).obj U := ProfiniteGrp.ofHom (Y := (diagram P).obj U) { toFun := QuotientGroup.mk @@ -154,7 +164,7 @@ def proj {P : ProfiniteGrp.{u}} (U : OpenNormalSubgroup P) : P ⟶ (diagram P).o } /-- The canonical cone over `diagram P` with point `P`. -/ -@[simps] +@[to_additive (attr := simps) /-- The canonical cone over `diagram P` with point `P`. -/] def cone (P : ProfiniteGrp.{u}) : Limits.Cone (diagram P) where pt := P π := { app := proj } diff --git a/Mathlib/Topology/Algebra/ConstMulAction.lean b/Mathlib/Topology/Algebra/ConstMulAction.lean index 0ebae3e977eda3..69f48dd54a30fe 100644 --- a/Mathlib/Topology/Algebra/ConstMulAction.lean +++ b/Mathlib/Topology/Algebra/ConstMulAction.lean @@ -92,22 +92,22 @@ theorem Filter.Tendsto.const_smul {f : β → α} {l : Filter β} {a : α} (hf : variable [TopologicalSpace β] {g : β → α} {b : β} {s : Set β} -@[to_additive] +@[to_fun (attr := to_additive (attr := fun_prop))] nonrec theorem ContinuousWithinAt.const_smul (hg : ContinuousWithinAt g s b) (c : M) : - ContinuousWithinAt (fun x => c • g x) s b := + ContinuousWithinAt (c • g) s b := hg.const_smul c -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] nonrec theorem ContinuousAt.const_smul (hg : ContinuousAt g b) (c : M) : - ContinuousAt (fun x => c • g x) b := + ContinuousAt (c • g) b := hg.const_smul c -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousOn.const_smul (hg : ContinuousOn g s) (c : M) : - ContinuousOn (fun x => c • g x) s := fun x hx => (hg x hx).const_smul c + ContinuousOn (c • g) s := fun x hx => (hg x hx).const_smul c -@[to_additive (attr := continuity, fun_prop)] -theorem Continuous.const_smul (hg : Continuous g) (c : M) : Continuous fun x => c • g x := +@[to_fun (attr := to_additive (attr := continuity, fun_prop))] +theorem Continuous.const_smul (hg : Continuous g) (c : M) : Continuous (c • g) := (continuous_const_smul _).comp hg /-- If a scalar is central, then its right action is continuous when its left action is. -/ @@ -157,7 +157,7 @@ theorem Topology.IsInducing.continuousConstSMul {N β : Type*} [SMul N β] [Topo {g : β → α} (hg : IsInducing g) (f : N → M) (hf : ∀ {c : N} {x : β}, g (c • x) = f c • g x) : ContinuousConstSMul N β where continuous_const_smul c := by - simpa only [Function.comp_def, hf, hg.continuous_iff] using hg.continuous.const_smul (f c) + simpa only [Function.comp_def, hf, hg.continuous_iff] using hg.continuous.fun_const_smul (f c) @[to_additive] theorem smul_closure_subset (c : M) (s : Set α) : c • closure s ⊆ closure (c • s) := diff --git a/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean b/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean index 46df6b9ac4ba7a..e0f4fb8983310f 100644 --- a/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean +++ b/Mathlib/Topology/Algebra/ContinuousAffineEquiv.lean @@ -97,18 +97,6 @@ theorem coe_coe (e : P₁ ≃ᴬ[k] P₂) : ⇑(e : P₁ ≃ᵃ[k] P₂) = e := theorem coe_toEquiv (e : P₁ ≃ᴬ[k] P₂) : ⇑e.toEquiv = e := 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 (e : P₁ ≃ᴬ[k] P₂) : P₁ → P₂ := - e - -/-- See Note [custom simps projection]. -/ -def Simps.symm_apply (e : P₁ ≃ᴬ[k] P₂) : P₂ → P₁ := - e.symm - -initialize_simps_projections ContinuousAffineEquiv (toFun → apply, invFun → symm_apply) - @[ext] theorem ext {e e' : P₁ ≃ᴬ[k] P₂} (h : ∀ x, e x = e' x) : e = e' := DFunLike.ext _ _ h @@ -178,6 +166,18 @@ def symm (e : P₁ ≃ᴬ[k] P₂) : P₂ ≃ᴬ[k] P₁ where continuous_toFun := e.continuous_invFun continuous_invFun := e.continuous_toFun +/-- 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 (e : P₁ ≃ᴬ[k] P₂) : P₁ → P₂ := + e + +/-- See Note [custom simps projection]. -/ +def Simps.symm_apply (e : P₁ ≃ᴬ[k] P₂) : P₂ → P₁ := + e.symm + +initialize_simps_projections ContinuousAffineEquiv (toFun → apply, invFun → symm_apply) + @[simp] theorem toAffineEquiv_symm (e : P₁ ≃ᴬ[k] P₂) : e.symm.toAffineEquiv = e.toAffineEquiv.symm := rfl @@ -309,6 +309,60 @@ end ReflSymmTrans section +variable (k) +variable [TopologicalSpace V₁] [IsTopologicalAddTorsor P₁] + +/-- The affine homeomorphism `V ≃ᴬ[k] P` given by `v ↦ v +ᵥ p`. This is `Equiv.vaddConst` +as a `ContinuousAffineEquiv`. -/ +@[simps! apply symm_apply] +def vaddConst (p : P₁) : V₁ ≃ᴬ[k] P₁ where + __ := AffineEquiv.vaddConst k p + __ := Homeomorph.vaddConst p + +@[simp] +lemma toAffineEquiv_vaddConst {p : P₁} : vaddConst k p = AffineEquiv.vaddConst k p := rfl + +/-- The affine homeomorphism given by `p' ↦ p -ᵥ p'`. This is `Equiv.constVSub` as a +`ContinuousAffineEquiv`. -/ +@[simps! apply symm_apply] +def constVSub (p : P₁) : P₁ ≃ᴬ[k] V₁ where + __ := AffineEquiv.constVSub k p + __ := Homeomorph.constVSub p + +@[simp] +lemma toAffineEquiv_constVSub {p : P₁} : constVSub k p = AffineEquiv.constVSub k p := rfl + +/-- The affine homeomorphism given by reflection about the point `x`. +This is `Equiv.pointReflection` as a `ContinuousAffineEquiv`. -/ +def pointReflection (x : P₁) : P₁ ≃ᴬ[k] P₁ := + (constVSub k x).trans (vaddConst k x) + +@[simp] +lemma coe_pointReflection (x : P₁) : + (pointReflection k x : P₁ → P₁) = Equiv.pointReflection x := rfl + +theorem pointReflection_apply (x y : P₁) : pointReflection k x y = (x -ᵥ y) +ᵥ x := + rfl + +@[simp] +theorem pointReflection_symm (x : P₁) : (pointReflection k x).symm = pointReflection k x := + toAffineEquiv_injective <| AffineEquiv.pointReflection_symm k x + +@[simp] +theorem toAffineEquiv_pointReflection (x : P₁) : + (pointReflection k x).toAffineEquiv = AffineEquiv.pointReflection k x := + rfl + +theorem pointReflection_self (x : P₁) : pointReflection k x x = x := + vsub_vadd _ _ + +theorem pointReflection_involutive (x : P₁) : Involutive (pointReflection k x : P₁ → P₁) := + Equiv.pointReflection_involutive x + +end + +section + variable {E F : Type*} [AddCommGroup E] [Module k E] [TopologicalSpace E] [AddCommGroup F] [Module k F] [TopologicalSpace F] diff --git a/Mathlib/Topology/Algebra/Group/Basic.lean b/Mathlib/Topology/Algebra/Group/Basic.lean index 62d93826965a48..7d82c9db6f5b06 100644 --- a/Mathlib/Topology/Algebra/Group/Basic.lean +++ b/Mathlib/Topology/Algebra/Group/Basic.lean @@ -410,7 +410,7 @@ end LatticeOps theorem Topology.IsInducing.continuousInv {G H : Type*} [Inv G] [Inv H] [TopologicalSpace G] [TopologicalSpace H] [ContinuousInv H] {f : G → H} (hf : IsInducing f) (hf_inv : ∀ x, f x⁻¹ = (f x)⁻¹) : ContinuousInv G := - ⟨hf.continuous_iff.2 <| by simpa only [Function.comp_def, hf_inv] using hf.continuous.inv⟩ + ⟨hf.continuous_iff.2 <| by simpa only [Function.comp_def, hf_inv] using hf.continuous.fun_inv⟩ section IsTopologicalGroup @@ -465,7 +465,7 @@ section ZPow @[to_additive (attr := continuity, fun_prop)] theorem continuous_zpow : ∀ z : ℤ, Continuous fun a : G => a ^ z | Int.ofNat n => by simpa using continuous_pow n - | Int.negSucc n => by simpa using (continuous_pow (n + 1)).inv + | Int.negSucc n => by simpa using (continuous_pow (n + 1)).fun_inv instance AddGroup.continuousConstSMul_int {A} [AddGroup A] [TopologicalSpace A] [IsTopologicalAddGroup A] : ContinuousConstSMul ℤ A := @@ -475,8 +475,8 @@ instance AddGroup.continuousSMul_int {A} [AddGroup A] [TopologicalSpace A] [IsTopologicalAddGroup A] : ContinuousSMul ℤ A := ⟨continuous_prod_of_discrete_left.mpr continuous_zsmul⟩ -@[to_additive (attr := continuity, fun_prop)] -theorem Continuous.zpow {f : α → G} (h : Continuous f) (z : ℤ) : Continuous fun b => f b ^ z := +@[to_fun (attr := to_additive (attr := continuity, fun_prop))] +theorem Continuous.zpow {f : α → G} (h : Continuous f) (z : ℤ) : Continuous (f ^ z) := (continuous_zpow z).comp h @[to_additive] @@ -492,19 +492,19 @@ theorem Filter.Tendsto.zpow {α} {l : Filter α} {f : α → G} {x : G} (hf : Te (z : ℤ) : Tendsto (fun x => f x ^ z) l (𝓝 (x ^ z)) := (continuousAt_zpow _ _).tendsto.comp hf -@[to_additive] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousWithinAt.zpow {f : α → G} {x : α} {s : Set α} (hf : ContinuousWithinAt f s x) - (z : ℤ) : ContinuousWithinAt (fun x => f x ^ z) s x := + (z : ℤ) : ContinuousWithinAt (f ^ z) s x := Filter.Tendsto.zpow hf z -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousAt.zpow {f : α → G} {x : α} (hf : ContinuousAt f x) (z : ℤ) : - ContinuousAt (fun x => f x ^ z) x := + ContinuousAt (f ^ z) x := Filter.Tendsto.zpow hf z -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousOn.zpow {f : α → G} {s : Set α} (hf : ContinuousOn f s) (z : ℤ) : - ContinuousOn (fun x => f x ^ z) s := fun x hx => (hf x hx).zpow z + ContinuousOn (f ^ z) s := fun x hx => (hf x hx).zpow z end ZPow @@ -914,6 +914,13 @@ lemma MonoidHom.isOpenQuotientMap_of_isQuotientMap {A : Type*} [Group A] use x * k, hx rw [map_mul, hk, mul_one] +@[to_additive] +lemma MonoidHom.isOpenQuotientMap_iff_isQuotientMap {A : Type*} [Group A] + [TopologicalSpace A] [ContinuousMul A] {B : Type*} [Group B] [TopologicalSpace B] + {F : Type*} [FunLike F A B] [MonoidHomClass F A B] {φ : F} : + IsOpenQuotientMap φ ↔ IsQuotientMap φ := + ⟨fun hf => hf.isQuotientMap, MonoidHom.isOpenQuotientMap_of_isQuotientMap⟩ + @[to_additive] theorem IsTopologicalGroup.ext {G : Type*} [Group G] {t t' : TopologicalSpace G} (tg : @IsTopologicalGroup G t _) (tg' : @IsTopologicalGroup G t' _) diff --git a/Mathlib/Topology/Algebra/Group/Defs.lean b/Mathlib/Topology/Algebra/Group/Defs.lean index 6a769265aea264..714d597f316ab4 100644 --- a/Mathlib/Topology/Algebra/Group/Defs.lean +++ b/Mathlib/Topology/Algebra/Group/Defs.lean @@ -72,21 +72,21 @@ theorem Filter.Tendsto.inv {f : α → G} {l : Filter α} {y : G} (h : Tendsto f variable {f : X → G} {s : Set X} {x : X} -@[to_additive (attr := continuity, fun_prop)] -theorem Continuous.inv (hf : Continuous f) : Continuous fun x => (f x)⁻¹ := +@[to_fun (attr := to_additive (attr := continuity, fun_prop))] +theorem Continuous.inv (hf : Continuous f) : Continuous f⁻¹ := continuous_inv.comp hf -@[to_additive] +@[to_fun (attr := to_additive (attr := fun_prop))] nonrec theorem ContinuousWithinAt.inv (hf : ContinuousWithinAt f s x) : - ContinuousWithinAt (fun x => (f x)⁻¹) s x := + ContinuousWithinAt f⁻¹ s x := hf.inv -@[to_additive (attr := fun_prop)] -nonrec theorem ContinuousAt.inv (hf : ContinuousAt f x) : ContinuousAt (fun x => (f x)⁻¹) x := +@[to_fun (attr := to_additive (attr := fun_prop))] +nonrec theorem ContinuousAt.inv (hf : ContinuousAt f x) : ContinuousAt f⁻¹ x := hf.inv -@[to_additive (attr := fun_prop)] -theorem ContinuousOn.inv (hf : ContinuousOn f s) : ContinuousOn (fun x => (f x)⁻¹) s := fun x hx ↦ +@[to_fun (attr := to_additive (attr := fun_prop))] +theorem ContinuousOn.inv (hf : ContinuousOn f s) : ContinuousOn f⁻¹ s := fun x hx ↦ (hf x hx).inv end ContinuousInv @@ -144,22 +144,22 @@ theorem Filter.Tendsto.div' {f g : α → G} {l : Filter α} {a b : G} (hf : Ten variable {f g : X → G} {s : Set X} {x : X} -@[to_additive (attr := fun_prop) sub] +@[to_additive (attr := to_fun (attr := fun_prop)) sub] nonrec theorem ContinuousAt.div' (hf : ContinuousAt f x) (hg : ContinuousAt g x) : - ContinuousAt (fun x => f x / g x) x := + ContinuousAt (f / g) x := hf.div' hg -@[to_additive sub] +@[to_additive (attr := to_fun (attr := fun_prop)) sub] theorem ContinuousWithinAt.div' (hf : ContinuousWithinAt f s x) (hg : ContinuousWithinAt g s x) : - ContinuousWithinAt (fun x => f x / g x) s x := + ContinuousWithinAt (f / g) s x := Filter.Tendsto.div' hf hg -@[to_additive (attr := fun_prop) sub] +@[to_additive (attr := to_fun (attr := fun_prop)) sub] theorem ContinuousOn.div' (hf : ContinuousOn f s) (hg : ContinuousOn g s) : - ContinuousOn (fun x => f x / g x) s := fun x hx => (hf x hx).div' (hg x hx) + ContinuousOn (f / g) s := fun x hx => (hf x hx).div' (hg x hx) -@[to_additive (attr := continuity, fun_prop) sub] -theorem Continuous.div' (hf : Continuous f) (hg : Continuous g) : Continuous fun x => f x / g x := +@[to_additive (attr := to_fun (attr := continuity, fun_prop)) sub] +theorem Continuous.div' (hf : Continuous f) (hg : Continuous g) : Continuous (f / g) := continuous_div'.comp₂ hf hg end ContinuousDiv diff --git a/Mathlib/Topology/Algebra/Group/Torsor.lean b/Mathlib/Topology/Algebra/Group/Torsor.lean index cb5470f71e0f73..e06ff5bde26c9c 100644 --- a/Mathlib/Topology/Algebra/Group/Torsor.lean +++ b/Mathlib/Topology/Algebra/Group/Torsor.lean @@ -98,6 +98,25 @@ theorem IsTopologicalTorsor.to_isTopologicalGroup : IsTopologicalGroup V where def Homeomorph.smulConst (p : P) : V ≃ₜ P where __ := Equiv.smulConst p +/-- The map `p' ↦ p /ₛ p'` as a homeomorphism: `Equiv.constSDiv` as a homeomorphism -/ +@[to_additive (attr := simps!) +/-- The map `p' ↦ p -ᵥ p'` as a homeomorphism: `Equiv.constVSub` as a homeomorphism -/] +def Homeomorph.constSDiv (p : P) : P ≃ₜ V where + toEquiv := Equiv.constSDiv p + continuous_invFun := by + have := IsTopologicalTorsor.to_isTopologicalGroup V P + fun_prop + +/-- `Equiv.pointReflection` as a homeomorphism -/ +def Homeomorph.pointReflection {V P : Type*} [AddGroup V] [TopologicalSpace V] [AddTorsor V P] + [TopologicalSpace P] [IsTopologicalAddTorsor P] (p : P) : P ≃ₜ P := + (Homeomorph.constVSub p).trans (Homeomorph.vaddConst p) + +@[simp] +lemma Homeomorph.coe_pointReflection {V P : Type*} [AddGroup V] [TopologicalSpace V] [AddTorsor V P] + [TopologicalSpace P] [IsTopologicalAddTorsor P] (p : P) : + (Homeomorph.pointReflection p : P → P) = Equiv.pointReflection p := rfl + end Torsor section Group diff --git a/Mathlib/Topology/Algebra/GroupCompletion.lean b/Mathlib/Topology/Algebra/GroupCompletion.lean index 6f0c55b83a8665..82cc3cda791079 100644 --- a/Mathlib/Topology/Algebra/GroupCompletion.lean +++ b/Mathlib/Topology/Algebra/GroupCompletion.lean @@ -70,12 +70,11 @@ open UniformSpace section Zero instance [UniformSpace α] [MonoidWithZero M] [Zero α] [MulActionWithZero M α] - [UniformContinuousConstSMul M α] : MulActionWithZero M (Completion α) := - { (inferInstance : MulAction M <| Completion α) with - smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, MulActionWithZero.smul_zero r] - zero_smul := - ext' (continuous_const_smul _) continuous_const fun a ↦ by - rw [← coe_smul, zero_smul, coe_zero] } + [UniformContinuousConstSMul M α] : MulActionWithZero M (Completion α) where + smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, MulActionWithZero.smul_zero r] + zero_smul := + ext' (continuous_const_smul _) continuous_const fun a ↦ by + rw [← coe_smul, zero_smul, coe_zero] end Zero @@ -95,82 +94,73 @@ theorem coe_sub (a b : α) : ((a - b : α) : Completion α) = a - b := theorem coe_add (a b : α) : ((a + b : α) : Completion α) = a + b := (map₂_coe_coe a b (· + ·) uniformContinuous_add).symm -instance : AddMonoid (Completion α) := - { (inferInstance : Zero <| Completion α), - (inferInstance : Add <| Completion α) with - zero_add := fun a ↦ - Completion.induction_on a - (isClosed_eq (continuous_map₂ continuous_const continuous_id) continuous_id) fun a ↦ - show 0 + (a : Completion α) = a by rw [← coe_zero, ← coe_add, zero_add] - add_zero := fun a ↦ - Completion.induction_on a - (isClosed_eq (continuous_map₂ continuous_id continuous_const) continuous_id) fun a ↦ - show (a : Completion α) + 0 = a by rw [← coe_zero, ← coe_add, add_zero] - add_assoc := fun a b c ↦ - Completion.induction_on₃ a b c - (isClosed_eq - (continuous_map₂ (continuous_map₂ continuous_fst (by fun_prop)) (by fun_prop)) - (continuous_map₂ continuous_fst (continuous_map₂ (by fun_prop) (by fun_prop)))) - fun a b c ↦ - show (a : Completion α) + b + c = a + (b + c) by repeat' rw_mod_cast [add_assoc] - nsmul := (· • ·) - nsmul_zero := fun a ↦ - Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦ - show 0 • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul] - nsmul_succ := fun n a ↦ - Completion.induction_on a - (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦ - show (n + 1) • (a : Completion α) = n • (a : Completion α) + (a : Completion α) by - rw [← coe_smul, succ_nsmul, coe_add, coe_smul] } - -instance : SubNegMonoid (Completion α) := - { (inferInstance : AddMonoid <| Completion α), - (inferInstance : Neg <| Completion α), - (inferInstance : Sub <| Completion α) with - sub_eq_add_neg := fun a b ↦ - Completion.induction_on₂ a b - (isClosed_eq (continuous_map₂ continuous_fst continuous_snd) - (continuous_map₂ continuous_fst (Completion.continuous_map.comp continuous_snd))) - fun a b ↦ mod_cast congr_arg ((↑) : α → Completion α) (sub_eq_add_neg a b) - zsmul := (· • ·) - zsmul_zero' := fun a ↦ - Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦ - show (0 : ℤ) • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul] - zsmul_succ' := fun n a ↦ - Completion.induction_on a - (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦ - show (n.succ : ℤ) • (a : Completion α) = _ by - rw [← coe_smul, show (n.succ : ℤ) • a = (n : ℤ) • a + a from - SubNegMonoid.zsmul_succ' n a, coe_add, coe_smul] - zsmul_neg' := fun n a ↦ - Completion.induction_on a - (isClosed_eq continuous_map <| Completion.continuous_map.comp continuous_map) fun a ↦ - show (Int.negSucc n) • (a : Completion α) = _ by - rw [← coe_smul, show (Int.negSucc n) • a = -((n.succ : ℤ) • a) from - SubNegMonoid.zsmul_neg' n a, coe_neg, coe_smul] } - -instance addGroup : AddGroup (Completion α) := - { (inferInstance : SubNegMonoid <| Completion α) with - neg_add_cancel := fun a ↦ - Completion.induction_on a - (isClosed_eq (continuous_map₂ Completion.continuous_map continuous_id) continuous_const) - fun a ↦ - show -(a : Completion α) + a = 0 by - rw_mod_cast [neg_add_cancel] - rfl } +instance : AddMonoid (Completion α) where + zero_add a := + Completion.induction_on a + (isClosed_eq (continuous_map₂ continuous_const continuous_id) continuous_id) fun a ↦ + show 0 + (a : Completion α) = a by rw [← coe_zero, ← coe_add, zero_add] + add_zero a := + Completion.induction_on a + (isClosed_eq (continuous_map₂ continuous_id continuous_const) continuous_id) fun a ↦ + show (a : Completion α) + 0 = a by rw [← coe_zero, ← coe_add, add_zero] + add_assoc := fun a b c ↦ + Completion.induction_on₃ a b c + (isClosed_eq + (continuous_map₂ (continuous_map₂ continuous_fst (by fun_prop)) (by fun_prop)) + (continuous_map₂ continuous_fst (continuous_map₂ (by fun_prop) (by fun_prop)))) + fun a b c ↦ + show (a : Completion α) + b + c = a + (b + c) by repeat' rw_mod_cast [add_assoc] + nsmul_zero a := + Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦ + show 0 • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul] + nsmul_succ n a := + Completion.induction_on a + (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦ + show (n + 1) • (a : Completion α) = n • (a : Completion α) + (a : Completion α) by + rw [← coe_smul, succ_nsmul, coe_add, coe_smul] + +instance : SubNegMonoid (Completion α) where + sub_eq_add_neg a b := + Completion.induction_on₂ a b + (isClosed_eq (continuous_map₂ continuous_fst continuous_snd) + (continuous_map₂ continuous_fst (Completion.continuous_map.comp continuous_snd))) + fun a b ↦ mod_cast congr_arg ((↑) : α → Completion α) (sub_eq_add_neg a b) + zsmul_zero' a := + Completion.induction_on a (isClosed_eq continuous_map continuous_const) fun a ↦ + show (0 : ℤ) • (a : Completion α) = 0 by rw [← coe_smul, ← coe_zero, zero_smul] + zsmul_succ' n a := + Completion.induction_on a + (isClosed_eq continuous_map <| continuous_map₂ continuous_map continuous_id) fun a ↦ + show (n.succ : ℤ) • (a : Completion α) = _ by + rw [← coe_smul, show (n.succ : ℤ) • a = (n : ℤ) • a + a from + SubNegMonoid.zsmul_succ' n a, coe_add, coe_smul] + zsmul_neg' n a := + Completion.induction_on a + (isClosed_eq continuous_map <| Completion.continuous_map.comp continuous_map) fun a ↦ + show (Int.negSucc n) • (a : Completion α) = _ by + rw [← coe_smul, show (Int.negSucc n) • a = -((n.succ : ℤ) • a) from + SubNegMonoid.zsmul_neg' n a, coe_neg, coe_smul] + +instance addGroup : AddGroup (Completion α) where + neg_add_cancel a := + Completion.induction_on a + (isClosed_eq (continuous_map₂ Completion.continuous_map continuous_id) continuous_const) + fun a ↦ + show -(a : Completion α) + a = 0 by + rw_mod_cast [neg_add_cancel] + rfl instance isUniformAddGroup : IsUniformAddGroup (Completion α) := ⟨uniformContinuous_map₂ Sub.sub⟩ instance {M} [Monoid M] [DistribMulAction M α] [UniformContinuousConstSMul M α] : - DistribMulAction M (Completion α) := - { (inferInstance : MulAction M <| Completion α) with - smul_add := fun r x y ↦ - induction_on₂ x y - (isClosed_eq ((continuous_fst.add continuous_snd).const_smul _) - ((continuous_fst.const_smul _).add (continuous_snd.const_smul _))) - fun a b ↦ by simp only [← coe_add, ← coe_smul, smul_add] - smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, smul_zero r] } + DistribMulAction M (Completion α) where + smul_add r x y := + induction_on₂ x y + (isClosed_eq ((continuous_fst.fun_add continuous_snd).fun_const_smul _) + ((continuous_fst.fun_const_smul _).fun_add (continuous_snd.fun_const_smul _))) + fun a b ↦ by simp only [← coe_add, ← coe_smul, smul_add] + smul_zero := fun r ↦ by rw [← coe_zero, ← coe_smul, smul_zero r] /-- The map from a group to its completion as a group hom. -/ @[simps] diff --git a/Mathlib/Topology/Algebra/GroupWithZero.lean b/Mathlib/Topology/Algebra/GroupWithZero.lean index 08fe633b384d6e..f3e4162725415f 100644 --- a/Mathlib/Topology/Algebra/GroupWithZero.lean +++ b/Mathlib/Topology/Algebra/GroupWithZero.lean @@ -112,22 +112,23 @@ theorem Filter.Tendsto.inv₀ {a : G₀} (hf : Tendsto f l (𝓝 a)) (ha : a ≠ variable [TopologicalSpace α] +@[to_fun (attr := fun_prop)] nonrec theorem ContinuousWithinAt.inv₀ (hf : ContinuousWithinAt f s a) (ha : f a ≠ 0) : - ContinuousWithinAt (fun x => (f x)⁻¹) s a := + ContinuousWithinAt f⁻¹ s a := hf.inv₀ ha -@[fun_prop] +@[to_fun (attr := fun_prop)] nonrec theorem ContinuousAt.inv₀ (hf : ContinuousAt f a) (ha : f a ≠ 0) : - ContinuousAt (fun x => (f x)⁻¹) a := + ContinuousAt f⁻¹ a := hf.inv₀ ha -@[continuity, fun_prop] -theorem Continuous.inv₀ (hf : Continuous f) (h0 : ∀ x, f x ≠ 0) : Continuous fun x => (f x)⁻¹ := +@[to_fun (attr := continuity, fun_prop)] +theorem Continuous.inv₀ (hf : Continuous f) (h0 : ∀ x, f x ≠ 0) : Continuous f⁻¹ := continuous_iff_continuousAt.2 fun x => (hf.tendsto x).inv₀ (h0 x) -@[fun_prop] +@[to_fun (attr := fun_prop)] theorem ContinuousOn.inv₀ (hf : ContinuousOn f s) (h0 : ∀ x ∈ s, f x ≠ 0) : - ContinuousOn (fun x => (f x)⁻¹) s := fun x hx => (hf x hx).inv₀ (h0 x hx) + ContinuousOn f⁻¹ s := fun x hx => (hf x hx).inv₀ (h0 x hx) end Inv₀ diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Field.lean b/Mathlib/Topology/Algebra/InfiniteSum/Field.lean index a053651031525a..67cf433b713286 100644 --- a/Mathlib/Topology/Algebra/InfiniteSum/Field.lean +++ b/Mathlib/Topology/Algebra/InfiniteSum/Field.lean @@ -22,7 +22,7 @@ public section section NormMulClass variable {α E : Type*} [SeminormedCommRing E] [NormMulClass E] [NormOneClass E] - {f : α → E} {x : E} + {f : α → E} {x : E} nonrec theorem HasProd.norm (hfx : HasProd f x) : HasProd (‖f ·‖) ‖x‖ := by simp only [HasProd, ← norm_prod] diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Order.lean b/Mathlib/Topology/Algebra/InfiniteSum/Order.lean index ddd26a88733d28..97017871e4fcc6 100644 --- a/Mathlib/Topology/Algebra/InfiniteSum/Order.lean +++ b/Mathlib/Topology/Algebra/InfiniteSum/Order.lean @@ -371,8 +371,8 @@ attribute [local instance] monadLiftOptionMetaM in This extension only proves non-negativity, strict positivity is more delicate for infinite sums and requires more assumptions. -/ @[positivity tsum _] -meta def evalTsum : PositivityExt where eval {u α} zα pα? e := do - let some pα := pα? | pure .none +meta def evalTsum : PositivityExt where eval {u α} zα pα? e := + match pα? with | none => pure .none | some pα => do match e with | ~q(@tsum _ $ι $instCommMonoid $instTopSpace $f $L) => lambdaBoundedTelescope f 1 fun args (body : Q($α)) => do diff --git a/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean b/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean index d806ef213421b3..e925b80f9c5ff0 100644 --- a/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean +++ b/Mathlib/Topology/Algebra/InfiniteSum/Ring.lean @@ -214,7 +214,7 @@ variable [TopologicalSpace α] [NonUnitalNonAssocSemiring α] {f g : A → α} theorem summable_mul_prod_iff_summable_mul_sigma_antidiagonal : (Summable fun x : A × A ↦ f x.1 * g x.2) ↔ Summable fun x : Σ n : A, antidiagonal n ↦ f (x.2 : A × A).1 * g (x.2 : A × A).2 := - Finset.sigmaAntidiagonalEquivProd.summable_iff.symm + Finset.HasAntidiagonal.sigmaAntidiagonalEquivProd.summable_iff.symm variable [T3Space α] [IsTopologicalSemiring α] @@ -226,7 +226,7 @@ theorem summable_sum_mul_antidiagonal_of_summable_mul exact h.sigma' fun n ↦ (hasSum_fintype _).summable /-- The **Cauchy product formula** for the product of two infinite sums indexed by `ℕ`, expressed -by summing on `Finset.antidiagonal`. +by summing on `Finset.HasAntidiagonal.antidiagonal`. See also `tsum_mul_tsum_eq_tsum_sum_antidiagonal_of_summable_norm` if `f` and `g` are absolutely summable. -/ @@ -234,7 +234,7 @@ protected theorem Summable.tsum_mul_tsum_eq_tsum_sum_antidiagonal (hf : Summable (hg : Summable g) (hfg : Summable fun x : A × A ↦ f x.1 * g x.2) : ((∑' n, f n) * ∑' n, g n) = ∑' n, ∑ kl ∈ antidiagonal n, f kl.1 * g kl.2 := by conv_rhs => congr; ext; rw [← Finset.sum_finset_coe, ← tsum_fintype (L := .unconditional _)] - rw [hf.tsum_mul_tsum hg hfg, ← sigmaAntidiagonalEquivProd.tsum_eq (_ : A × A → α)] + rw [hf.tsum_mul_tsum hg hfg, ← HasAntidiagonal.sigmaAntidiagonalEquivProd.tsum_eq (_ : A × A → α)] exact (summable_mul_prod_iff_summable_mul_sigma_antidiagonal.mp hfg).tsum_sigma' (fun n ↦ (hasSum_fintype _).summable) diff --git a/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean b/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean index 85e6942e6f3c1c..293cc2d1ff385e 100644 --- a/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean +++ b/Mathlib/Topology/Algebra/InfiniteSum/SummationFilter.lean @@ -220,9 +220,8 @@ instance : (conditional β).LeAtTop := ⟨support_eq_univ_iff.mp <| by simpa [eq_univ_iff_forall, support, -eventually_and] using! fun x ↦ prod_mem_prod (eventually_le_atBot x) (eventually_ge_atTop x)⟩ -set_option linter.flexible false in -- simp followed by infer_instance instance [Nonempty β] [IsDirectedOrder β] [IsCodirectedOrder β] : (conditional β).NeBot := - ⟨by simp; infer_instance⟩ + ⟨by rw [conditional_filter]; infer_instance⟩ instance [IsCountablyGenerated (atTop : Filter β)] [IsCountablyGenerated (atBot : Filter β)] : IsCountablyGenerated (conditional β).filter := diff --git a/Mathlib/Topology/Algebra/IsUniformGroup/Basic.lean b/Mathlib/Topology/Algebra/IsUniformGroup/Basic.lean index a00d7c6846f480..ac1b694b34f284 100644 --- a/Mathlib/Topology/Algebra/IsUniformGroup/Basic.lean +++ b/Mathlib/Topology/Algebra/IsUniformGroup/Basic.lean @@ -287,8 +287,8 @@ instance Subgroup.isClosed_of_discrete [T2Space G] {H : Subgroup G} [DiscreteTop @[to_additive] lemma Subgroup.tendsto_coe_cofinite_of_discrete [T2Space G] (H : Subgroup G) (hH : IsDiscrete (H : Set G)) : Tendsto ((↑) : H → G) cofinite (cocompact _) := - haveI : DiscreteTopology H := isDiscrete_iff_discreteTopology.mp hH - IsClosed.tendsto_coe_cofinite_of_isDiscrete isClosed_of_discrete hH + haveI : DiscreteTopology H := isDiscrete_iff_discreteTopology.mp hH + IsClosed.tendsto_coe_cofinite_of_isDiscrete isClosed_of_discrete hH @[to_additive] lemma MonoidHom.tendsto_coe_cofinite_of_discrete [T2Space G] {H : Type*} [Group H] {f : H →* G} diff --git a/Mathlib/Topology/Algebra/LinearMapCompletion.lean b/Mathlib/Topology/Algebra/LinearMapCompletion.lean index 9506ff449605a0..65520a2f0cada7 100644 --- a/Mathlib/Topology/Algebra/LinearMapCompletion.lean +++ b/Mathlib/Topology/Algebra/LinearMapCompletion.lean @@ -42,7 +42,7 @@ noncomputable def completion (f : α →SL[σ] β) : Completion α →SL[σ] Com induction x using induction_on with | hp => exact isClosed_eq (continuous_map.comp <| continuous_const_smul r) - (continuous_map.const_smul _) + (continuous_map.fun_const_smul _) | ih x => simp [← Completion.coe_smul] @[simp] diff --git a/Mathlib/Topology/Algebra/Module/Complement.lean b/Mathlib/Topology/Algebra/Module/Complement.lean index 74c43d16d84cce..68b2429b536667 100644 --- a/Mathlib/Topology/Algebra/Module/Complement.lean +++ b/Mathlib/Topology/Algebra/Module/Complement.lean @@ -284,7 +284,7 @@ lemma projectionL_eq_id_sub_projectionL [IsTopologicalAddGroup M] (h : IsTopComp ContinuousLinearMap.ext <| projectionL_eq_self_sub_projectionL h /-- The projection to `p` along `q` of `x` equals `x` if and only if `x ∈ p`. -/ -lemma projectionL_eq_self_iff [ContinuousSub M] (h : IsTopCompl p q) (x : M) : +lemma projectionL_eq_self_iff (h : IsTopCompl p q) (x : M) : p.projectionL q h x = x ↔ x ∈ p := projection_eq_self_iff h.isCompl x diff --git a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean index f0f7d0b1425042..b05b66b66b0ea6 100644 --- a/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean +++ b/Mathlib/Topology/Algebra/Module/ContinuousLinearMap/Basic.lean @@ -450,7 +450,6 @@ instance : AddMonoid (M₁ →SL[σ₁₂] M₂) where intros ext apply_rules [zero_add, add_assoc, add_zero, neg_add_cancel, add_comm] - nsmul := (· • ·) nsmul_zero f := by ext simp @@ -883,7 +882,6 @@ instance : IsSubApply (M →SL[σ₁₂] M₂) M M₂ where -- Todo: figure out how to use `FunLike.addCommGroup` here instance addCommGroup : AddCommGroup (M →SL[σ₁₂] M₂) where sub_eq_add_neg _ _ := by ext; apply sub_eq_add_neg - zsmul := (· • ·) zsmul_zero' f := by ext; simp zsmul_succ' n f := by ext; simp [add_smul, add_comm] zsmul_neg' n f := by ext; simp [add_smul] diff --git a/Mathlib/Topology/Algebra/Module/Equiv.lean b/Mathlib/Topology/Algebra/Module/Equiv.lean index afc135bda2246b..b3ade728418b0f 100644 --- a/Mathlib/Topology/Algebra/Module/Equiv.lean +++ b/Mathlib/Topology/Algebra/Module/Equiv.lean @@ -12,8 +12,26 @@ public import Mathlib.Topology.Algebra.Module.ContinuousLinearMap.Restrict /-! # Continuous linear equivalences +## Notation Continuous semilinear / linear / star-linear equivalences between topological modules are denoted by `M ≃SL[σ] M₂`, `M ≃L[R] M₂` and `M ≃L⋆[R] M₂`. + +## Main Definitions +* `toHomeomorph` is the homeomorphism induced by a continuous (semi)linear equivalence. +* `symm` is the inverse of a continuous linear equivalence as a continuous linear equivalence. +* `equivOfInverse` creates a `ContinuousLinearEquiv` from two `ContinuousLinearMap`s that are + inverse of each other (as functions). See also `equivOfInverse'` when they're inverse to each + other as continuous linear maps. +* `ofUnit` is the `ContinuousLinearEquiv` corresponding to a unit in the ring of continuous + endomorphisms. See `toUnit` for the inverse direction. +* `IsInvertible`: a continuous linear map is invertible if it is the forward direction of a + continuous linear equivalence. +* `ofIsHomeomorph`: a linear equivalence that is a homeomorphism is a continuous linear equivalence. + +## Main Results +* `prodComm`: the product of topological modules is commutative up to continuous linear isomorphism. +* `LinearEquiv.isHomeomorph_iff`: A linear equivalence between topological modules is a + homeomorphism if and only if it is continuous in both directions. -/ @[expose] public section @@ -26,8 +44,6 @@ open scoped Ring universe u v w u' -section - /-- Continuous linear equivalences between modules. We only put the type classes that are necessary for the definition, although in applications `M` and `M₂` will be topological modules over the topological semiring `R`. -/ @@ -223,7 +239,6 @@ theorem isClosed_image (e : M₁ ≃SL[σ₁₂] M₂) {s : Set M₁} : IsClosed theorem map_nhds_eq (e : M₁ ≃SL[σ₁₂] M₂) (x : M₁) : map e (𝓝 x) = 𝓝 (e x) := e.toHomeomorph.map_nhds_eq x --- Make some straightforward lemmas available to `simp`. theorem map_zero (e : M₁ ≃SL[σ₁₂] M₂) : e (0 : M₁) = 0 := (e : M₁ →SL[σ₁₂] M₂).map_zero @@ -280,10 +295,6 @@ def toContinuousAddEquiv (e : M₁ ≃L[R₁] M) : M₁ ≃ₜ+ M := @[simp] lemma toContinuousAddEquiv_coe (e : M₁ ≃L[R₁] M) : ⇑e.toContinuousAddEquiv = e := rfl -end - -section - variable (R₁ M₁) /-- The identity map as a continuous linear equivalence. -/ @@ -355,10 +366,8 @@ theorem trans_toLinearEquiv (e₁ : M₁ ≃SL[σ₁₂] M₂) (e₂ : M₂ ≃S /-- Product of two continuous linear equivalences. The map comes from `Equiv.prodCongr`. -/ def prodCongr [Module R₁ M₂] [Module R₁ M₃] [Module R₁ M₄] (e : M₁ ≃L[R₁] M₂) (e' : M₃ ≃L[R₁] M₄) : - (M₁ × M₃) ≃L[R₁] M₂ × M₄ := - { e.toLinearEquiv.prodCongr e'.toLinearEquiv with - continuous_toFun := e.continuous_toFun.prodMap e'.continuous_toFun - continuous_invFun := e.continuous_invFun.prodMap e'.continuous_invFun } + (M₁ × M₃) ≃L[R₁] M₂ × M₄ where + __ := e.toLinearEquiv.prodCongr e'.toLinearEquiv @[simp, norm_cast] theorem prodCongr_apply [Module R₁ M₂] [Module R₁ M₃] [Module R₁ M₄] (e : M₁ ≃L[R₁] M₂) @@ -371,6 +380,7 @@ theorem coe_prodCongr [Module R₁ M₂] [Module R₁ M₃] [Module R₁ M₄] ( (e.prodCongr e' : M₁ × M₃ →L[R₁] M₂ × M₄) = (e : M₁ →L[R₁] M₂).prodMap (e' : M₃ →L[R₁] M₄) := rfl +@[simp] theorem prodCongr_symm [Module R₁ M₂] [Module R₁ M₃] [Module R₁ M₄] (e : M₁ ≃L[R₁] M₂) (e' : M₃ ≃L[R₁] M₄) : (e.prodCongr e').symm = e.symm.prodCongr e'.symm := rfl @@ -378,7 +388,7 @@ theorem prodCongr_symm [Module R₁ M₂] [Module R₁ M₃] [Module R₁ M₄] variable (R₁ M₁ M₂) set_option backward.defeqAttrib.useBackward true in -/-- Product of modules is commutative up to continuous linear isomorphism. -/ +/-- Product of topological modules is commutative up to continuous linear isomorphism. -/ @[simps! apply toLinearEquiv] def prodComm [Module R₁ M₂] : (M₁ × M₂) ≃L[R₁] M₂ × M₁ where __ := LinearEquiv.prodComm R₁ M₁ M₂ @@ -611,7 +621,8 @@ protected theorem _root_.LinearEquiv.isUniformEmbedding {E₁ E₂ : Type*} [Uni E₁ ≃SL[σ₁₂] E₂) /-- Create a `ContinuousLinearEquiv` from two `ContinuousLinearMap`s that are -inverse of each other. See also `equivOfInverse'`. -/ +inverse of each other. See also `equivOfInverse'`. +*ToDo*: Improve the naiming to make it match `LinearMap.ofLinear`. -/ def equivOfInverse (f₁ : M₁ →SL[σ₁₂] M₂) (f₂ : M₂ →SL[σ₂₁] M₁) (h₁ : Function.LeftInverse f₂ f₁) (h₂ : Function.RightInverse f₂ f₁) : M₁ ≃SL[σ₁₂] M₂ := { f₁ with @@ -630,7 +641,8 @@ theorem symm_equivOfInverse (f₁ : M₁ →SL[σ₁₂] M₂) (f₂ h₁ h₂) rfl /-- Create a `ContinuousLinearEquiv` from two `ContinuousLinearMap`s that are -inverse of each other, in the `ContinuousLinearMap.comp` sense. See also `equivOfInverse`. -/ +inverse of each other, in the `ContinuousLinearMap.comp` sense. See also `equivOfInverse`. +*ToDo*: Improve the naiming to make it match `LinearMap.ofLinear` -/ def equivOfInverse' (f₁ : M₁ →SL[σ₁₂] M₂) (f₂ : M₂ →SL[σ₂₁] M₁) (h₁ : f₁.comp f₂ = .id R₂ M₂) (h₂ : f₂.comp f₁ = .id R₁ M₁) : M₁ ≃SL[σ₁₂] M₂ := equivOfInverse f₁ f₂ @@ -1412,15 +1424,34 @@ theorem smul_trans [SMulCommClass R S V] [IsScalarTower S R G] (α : Sˣ) (e : G theorem trans_smul [IsScalarTower S R G] (α : Sˣ) (e : G ≃L[R] V) (f : V ≃L[R] W) : e.trans (α • f) = α • (e.trans f) := by ext; simp -end ContinuousLinearEquiv +section IsHomeomorph + +variable {S₁ M M₁ : Type*} [Semiring S₁] {σ : S →+* S₁} {σ' : S₁ →+* S} + [RingHomInvPair σ σ'] [RingHomInvPair σ' σ] [TopologicalSpace M] [AddCommMonoid M] [Module S M] + [TopologicalSpace M₁] [AddCommMonoid M₁] [Module S₁ M₁] + +/-- A linear equivalence that is a homeomorphism is a continuous linear equivalence. -/ +def ofIsHomeomorph (f : M ≃ₛₗ[σ] M₁) (hf : IsHomeomorph f) : M ≃SL[σ] M₁ where + __ := f + continuous_toFun := hf.continuous + continuous_invFun := (f.isHomeomorph_iff.mp hf).2 + +theorem isHomeomorph (f : M ≃SL[σ] M₁) : IsHomeomorph f := ⟨f.continuous, isOpenMap f, f.bijective⟩ + +variable {f : M ≃ₛₗ[σ] M₁} (hf : IsHomeomorph f) + +@[simp] +lemma toLinearquiv_ofIsHomeomorph : (ofIsHomeomorph f hf).toLinearEquiv = f := by + dsimp only [ofIsHomeomorph] + +@[simp] +lemma coe_ofIsHomeomorph : (ofIsHomeomorph f hf : M → M₁) = f := by dsimp [ofIsHomeomorph] /-- A linear equivalence between topological modules is a homeomorphism if and only if it is continuous in both directions. -/ -theorem LinearEquiv.isHomeomorph_iff {R S : Type*} [Semiring R] [Semiring S] - {σ : R →+* S} {σ' : S →+* R} [RingHomInvPair σ σ'] [RingHomInvPair σ' σ] - {M : Type*} [TopologicalSpace M] [AddCommMonoid M] [Module R M] - {N : Type*} [TopologicalSpace N] [AddCommMonoid N] [Module S N] - (e : M ≃ₛₗ[σ] N) : IsHomeomorph e ↔ Continuous e ∧ Continuous e.symm := - e.toEquiv.isHomeomorph_iff +theorem _root_.LinearEquiv.isHomeomorph_iff (e : M ≃ₛₗ[σ] M₁) : + IsHomeomorph e ↔ Continuous e ∧ Continuous e.symm := e.toEquiv.isHomeomorph_iff -end +end IsHomeomorph + +end ContinuousLinearEquiv diff --git a/Mathlib/Topology/Algebra/Module/FiniteDimension.lean b/Mathlib/Topology/Algebra/Module/FiniteDimension.lean index 67c5ce664b99c6..9e16cef2540d80 100644 --- a/Mathlib/Topology/Algebra/Module/FiniteDimension.lean +++ b/Mathlib/Topology/Algebra/Module/FiniteDimension.lean @@ -10,6 +10,7 @@ public import Mathlib.Analysis.LocallyConvex.Bounded public import Mathlib.Analysis.Normed.Module.Basic public import Mathlib.Analysis.SpecificLimits.Normed public import Mathlib.LinearAlgebra.FiniteDimensional.Lemmas +public import Mathlib.RingTheory.Finiteness.Cofinite public import Mathlib.RingTheory.LocalRing.Basic public import Mathlib.Topology.Algebra.Module.Determinant public import Mathlib.Topology.Algebra.Module.ModuleTopology @@ -739,6 +740,14 @@ theorem Submodule.ClosedComplemented.of_finiteDimensional_quotient {p : Submodul alias Submodule.ClosedComplemented.of_quotient_finiteDimensional := Submodule.ClosedComplemented.of_finiteDimensional_quotient +theorem Submodule.ClosedComplemented.of_disjoint_of_finiteDimensional_quotient + {A B : Submodule 𝕜 E} [B_cofg : FiniteDimensional 𝕜 (E ⧸ B)] (hB : IsClosed (B : Set E)) + (hAB : Disjoint A B) : A.ClosedComplemented := by + obtain ⟨C, B_le_C, C_compl_A⟩ := hAB.symm.exists_isCompl + have C_cofg : FiniteDimensional 𝕜 (E ⧸ C) := CoFG.of_le B_le_C B_cofg + have hC : IsClosed (C : Set E) := isClosed_mono_of_finiteDimensional_quotient hB B_le_C + exact C_compl_A.isTopCompl_of_finiteDimensional_quotient hC |>.symm.closedComplemented + lemma Submodule.ClosedComplemented.of_finiteDimensional_of_le {A B : Submodule 𝕜 E} [FiniteDimensional 𝕜 A] (hA : A.ClosedComplemented) [T2Space A] (hB : B ≤ A) : B.ClosedComplemented := by diff --git a/Mathlib/Topology/Algebra/Module/IsWeak.lean b/Mathlib/Topology/Algebra/Module/IsWeak.lean new file mode 100644 index 00000000000000..841dc9ef2932b4 --- /dev/null +++ b/Mathlib/Topology/Algebra/Module/IsWeak.lean @@ -0,0 +1,180 @@ +/- +Copyright (c) 2026 Jireh Loreaux. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Jireh Loreaux +-/ +module + +public import Mathlib.Topology.Algebra.Module.ContinuousLinearMap.Basic +public import Mathlib.Topology.Algebra.Module.Equiv +public import Mathlib.LinearAlgebra.BilinearMap + +/-! # Weak topologies on modules + +Given a bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜`, the weak topology on `E` is the coarsest topology +such that for all `y : F` every map `(B · y)` is continuous; equivalently, it is the topology +on `E` induced by the map `(B · · : E → (F → 𝕜))`. + +This file defines a `Prop`-valued typeclass `LinearMap.IsWeak` expressing that an existing topology +on `E` is the weak topology. Although this could be passed around explicitly as a hypothesis +`Topology.IsInducing (B · ·)`, given the ubiquity of weak topologies in functional analysis, the +numerous properties that can be deduced because the inducing map `B` is bilinear, the fact that +several theorems (e.g., one version of the bipolar theorem) require this hypothesis, and we can +instantiate this class for several extant types in Mathlib, we choose to make this a typeclass +instead. + +Note that establishing `LinearMap.IsWeak` before proving theorems about a particular type can help +prevent abuse of definitional equalities. This because spaces equipped with a weak topology are +frequently type synonyms of some other type `E'`. For example, suppose `E'` is a type (potentially +with some extant topology other than the weak topology) and `B' : E' →ₗ[𝕜] F →ₗ[𝕜] 𝕜` is a +bilinear form. To consider the weak topology on `E'` induced by `B'`, in practice we must create a +type synonym `E` with an instance `TopologicalSpace E := .induced (B' · ·) Pi.topologicalSpace`. +It would then be tempting to create theorems such as: + +```lean +example (y : F) : Continuous (fun x : E ↦ B' x y) := sorry +``` + +However, this statement contains an abuse of the the definitional equality `E := E'` since `x : E`, +but `B'` has domain `E'`. Morever, one might be tempted to say that `B'.IsWeak`, but this is +impossible because the domain of `B'` is `E'`, which is equipped with the incorrect topology. +Instead, what one should do is to first define a new bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜` by +composing `B'` with the linear equivalence between `E` and `E'`, and then establish `B.IsWeak`. +If then one proves theorems about `E` using only the `LinearMap.IsWeak` API, then one can have more +confidence that the statements are type correct. + +## Main definitions + ++ `LinearMap.IsWeak`: a typeclass expressing that the topology on `E` is the weak topology induced + by the bilinear form `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜`. ++ `LinearMap.IsWeak.eval`: the evaluation map `F →ₗ[𝕜] StrongDual 𝕜 E` sending `y : F` to the + continuous linear functional `(B · y)`. + +## Main results + +We prove the following results characterizing the weak topology: + +* `LinearMap.IsWeak.continuous_eval`: For any `y : F`, the evaluation mapping `(B · y)` is + continuous. +* `LinearMap.IsWeak.continuous_of_continuous_eval`: For a mapping to `WeakBilin B` to be continuous, + it suffices that its compositions with pairing with `B` at all points `y : F` is continuous. +* `LinearMap.IsWeak.tendsto_iff_forall_eval_tendsto`: Convergence in `WeakBilin B` can be + characterized in terms of convergence of the evaluations at all points `y : F`. + +-/ + +@[expose] public section + +open Topology Filter + +section Basic + +variable {α 𝕜 E F E' F' : Type*} [CommSemiring 𝕜] [TopologicalSpace 𝕜] + [AddCommMonoid E] [Module 𝕜 E] + [AddCommMonoid F] [Module 𝕜 F] + +/-- Typeclass expressing that the topology on `E` is the weak topology induced +by the bilinear form `B`. -/ +@[mk_iff] +class LinearMap.IsWeak [t : TopologicalSpace E] (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) : Prop where + eq_induced : t = .induced (B · ·) Pi.topologicalSpace + +variable [inst : TopologicalSpace E] (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) [hB : B.IsWeak] + +namespace LinearMap.IsWeak + +instance : B.flip.flip.IsWeak := hB + +/-- The coercion `(B · ·) : E → (F → 𝕜)` is continuous. -/ +theorem coeFn_continuous : Continuous (B · ·) := + hB.eq_induced ▸ continuous_induced_dom + +/-- The evaluation map `(B · y) : E → 𝕜` is continuous for each `y : F`. -/ +@[fun_prop] +lemma continuous_eval (y : F) : Continuous (B · y) := + continuous_pi_iff.mp (coeFn_continuous B) _ + +/-- A map `f : α → E` is continuous if all the maps `fun a ↦ B (f a) y` are continuous +for each `y : F`. -/ +lemma continuous_of_continuous_eval {α : Type*} [TopologicalSpace α] + {f : α → E} (hf : ∀ y, Continuous (fun x ↦ B (f x) y)) : + Continuous f := + hB.eq_induced ▸ continuous_induced_rng.mpr (continuous_pi_iff.mpr hf) + +lemma continuous_iff {α : Type*} [TopologicalSpace α] {f : α → E} : + Continuous f ↔ ∀ y, Continuous (fun x ↦ B (f x) y) := + ⟨fun _ ↦ by fun_prop, hB.continuous_of_continuous_eval⟩ + +/-- The coercion `(B · ·) : E → (F → 𝕜)` is an embedding. -/ +theorem isInducing : IsInducing (B · ·) where + eq_induced := hB.eq_induced + +variable {B} in +/-- The coercion `(B · ·) : E → (F → 𝕜)` is an embedding. -/ +theorem isEmbedding (hB_inj : Function.Injective B) : + IsEmbedding (B · ·) := by + convert! (LinearMap.coe_injective.comp hB_inj |>.isEmbedding_induced) + exact hB.eq_induced + +variable {B} in +theorem tendsto_iff_forall_eval_tendsto {α : Type*} {l : Filter α} {f : α → E} {x : E} + (hB_inj : Function.Injective B) : + Tendsto f l (𝓝 x) ↔ ∀ y, Tendsto (fun i ↦ B (f i) y) l (𝓝 (B x y)) := by + rw [← tendsto_pi_nhds, (isEmbedding hB_inj).tendsto_nhds_iff, Function.comp_def] + +/-- Suppose `B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜` and `B' : E' →ₗ[𝕜] F' →ₗ[𝕜] 𝕜` are bilinear maps such that +`E ≃L[𝕜] E'` and `F ≃ₗ[𝕜] F'`. If `B.IsWeak`, then so also `B'.IsWeak`. -/ +protected theorem congr [AddCommMonoid E'] [Module 𝕜 E'] + [AddCommMonoid F'] [Module 𝕜 F'] [TopologicalSpace E'] + (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) (B' : E' →ₗ[𝕜] F' →ₗ[𝕜] 𝕜) (e : E ≃L[𝕜] E') (f : F ≃ₗ[𝕜] F') + (hBB' : e.toLinearEquiv.arrowCongr (f.arrowCongr (.refl ..)) B = B') [hB : B.IsWeak] : + B'.IsWeak where + eq_induced := by + rw [e.symm.toHomeomorph.induced_eq.symm] + apply congr(TopologicalSpace.induced e.symm $(hB.eq_induced)).trans + simp_rw [induced_compose, ← hBB', induced_to_pi] + rw [f.toEquiv.iInf_congr] + simp + +/-- Map `F` into the topological dual of `E` with the weak topology induced by `F` -/ +def eval [ContinuousAdd 𝕜] [ContinuousConstSMul 𝕜 𝕜] : F →ₗ[𝕜] StrongDual 𝕜 E where + toFun f := ⟨B.flip f, by fun_prop⟩ + map_add' _ _ := by ext; simp + map_smul' _ _ := by ext; simp + +include hB in +/-- Addition in `E` is continuous when `E` is equipped with a `LinearMap.IsWeak` topology. -/ +theorem continuousAdd [ContinuousAdd 𝕜] : ContinuousAdd E where + continuous_add := by + let t₁ : TopologicalSpace E := .induced (B · ·) Pi.topologicalSpace + have : B.IsWeak := ⟨rfl⟩ + rw [hB.eq_induced, continuous_induced_rng] + simp only [Function.comp_def, map_add, add_apply] + fun_prop + +include hB in +/-- Scalar multiplication in `E` is continuous when `E` is equipped with a `LinearMap.IsWeak` +topology. -/ +theorem continuousSMul [ContinuousSMul 𝕜 𝕜] : ContinuousSMul 𝕜 E where + continuous_smul := by + let t₁ : TopologicalSpace E := .induced (B · ·) Pi.topologicalSpace + have : B.IsWeak := ⟨rfl⟩ + rw [hB.eq_induced, continuous_induced_rng] + simp only [Function.comp_def, map_smul, smul_apply] + fun_prop + +/-- `E` is a `IsTopologicalAddGroup` when `E` is equipped with a `LinearMap.IsWeak` topology. -/ +theorem isTopologicalAddGroup {𝕜 E F : Type*} [CommRing 𝕜] [TopologicalSpace 𝕜] + [AddCommGroup E] [Module 𝕜 E] [AddCommGroup F] [Module 𝕜 F] [TopologicalSpace E] + [ContinuousAdd 𝕜] (B : E →ₗ[𝕜] F →ₗ[𝕜] 𝕜) [hB : B.IsWeak] : IsTopologicalAddGroup E where + toContinuousAdd := continuousAdd B + continuous_neg := by + let t₁ : TopologicalSpace E := .induced (B · ·) Pi.topologicalSpace + have : B.IsWeak := ⟨rfl⟩ + rw [hB.eq_induced, continuous_induced_rng, continuous_pi_iff] + simp_rw [Function.comp_apply, map_neg, neg_apply, ← map_neg (B _)] + fun_prop + +end LinearMap.IsWeak + +end Basic diff --git a/Mathlib/Topology/Algebra/Module/LocallyConvex.lean b/Mathlib/Topology/Algebra/Module/LocallyConvex.lean index a626fcaee69bcb..c73f0cfd0adb75 100644 --- a/Mathlib/Topology/Algebra/Module/LocallyConvex.lean +++ b/Mathlib/Topology/Algebra/Module/LocallyConvex.lean @@ -6,7 +6,8 @@ Authors: Anatole Dedecker module public import Mathlib.Analysis.Convex.Topology -public import Mathlib.Topology.Connected.LocPathConnected +public import Mathlib.Tactic.CrossRefAttribute +public import Mathlib.Topology.Connected.LocallyPathConnected public import Mathlib.Analysis.Convex.PathConnected /-! @@ -46,6 +47,7 @@ section Semimodule /-- A `LocallyConvexSpace` is a topological semimodule over an ordered semiring in which convex neighborhoods of a point form a neighborhood basis at that point. -/ +@[wikidata Q1572357] class LocallyConvexSpace (𝕜 E : Type*) [Semiring 𝕜] [PartialOrder 𝕜] [AddCommMonoid E] [Module 𝕜 E] [TopologicalSpace E] : Prop where convex_basis : ∀ x : E, (𝓝 x).HasBasis (fun s : Set E => s ∈ 𝓝 x ∧ Convex 𝕜 s) id @@ -100,14 +102,14 @@ theorem locallyConvexSpace_iff_exists_convex_subset_zero : (locallyConvexSpace_iff_zero 𝕜 E).trans hasBasis_self -- see Note [lower instance priority] -instance (priority := 100) LocallyConvexSpace.toLocPathConnectedSpace [Module ℝ E] - [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E] : LocPathConnectedSpace E := +instance (priority := 100) LocallyConvexSpace.toLocallyPathConnectedSpace [Module ℝ E] + [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E] : LocallyPathConnectedSpace E := .of_bases (fun x ↦ convex_basis (𝕜 := ℝ) x) fun _ _ hs ↦ hs.2.isPathConnected <| nonempty_of_mem <| mem_of_mem_nhds hs.1 /-- Convex subsets of locally convex spaces are locally path-connected. -/ -theorem Convex.locPathConnectedSpace [Module ℝ E] [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E] - {S : Set E} (hS : Convex ℝ S) : LocPathConnectedSpace S := by +theorem Convex.locallyPathConnectedSpace [Module ℝ E] [ContinuousSMul ℝ E] [LocallyConvexSpace ℝ E] + {S : Set E} (hS : Convex ℝ S) : LocallyPathConnectedSpace S := by refine ⟨fun x ↦ ⟨fun s ↦ ⟨fun hs ↦ ?_, fun ⟨t, ht⟩ ↦ mem_of_superset ht.1.1 ht.2⟩⟩⟩ let ⟨t, ht⟩ := (mem_nhds_subtype S x s).mp hs let ⟨t', ht'⟩ := (LocallyConvexSpace.convex_basis (𝕜 := ℝ) x.1).mem_iff.mp ht.1 @@ -116,6 +118,9 @@ theorem Convex.locPathConnectedSpace [Module ℝ E] [ContinuousSMul ℝ E] [Loca · refine Subtype.preimage_coe_self_inter _ _ ▸ IsPathConnected.preimage_coe ?_ inter_subset_left exact (hS.inter ht'.1.2).isPathConnected ⟨x, x.2, mem_of_mem_nhds ht'.1.1⟩ +@[deprecated (since := "2026-06-21")] +alias Convex.locPathConnectedSpace := Convex.locallyPathConnectedSpace + end Module section LinearOrderedField diff --git a/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean b/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean index 0f9c1b4807783e..e9e26963455cdd 100644 --- a/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean +++ b/Mathlib/Topology/Algebra/Module/Multilinear/Basic.lean @@ -126,9 +126,10 @@ instance : Zero (ContinuousMultilinearMap R M₁ M₂) := instance : Inhabited (ContinuousMultilinearMap R M₁ M₂) := ⟨0⟩ -@[simp] -theorem zero_apply (m : ∀ i, M₁ i) : (0 : ContinuousMultilinearMap R M₁ M₂) m = 0 := - rfl +instance : IsZeroApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where + zero_apply _ := rfl + +@[deprecated (since := "2026-06-10")] protected alias zero_apply := zero_apply @[simp] theorem toMultilinearMap_zero : (0 : ContinuousMultilinearMap R M₁ M₂).toMultilinearMap = 0 := @@ -143,10 +144,10 @@ variable {R' R'' A : Type*} [Semiring A] [∀ i, Module A (M₁ i)] instance : SMul R' (ContinuousMultilinearMap A M₁ M₂) := ⟨fun c f => { c • f.toMultilinearMap with cont := f.cont.const_smul c }⟩ -@[simp] -theorem smul_apply (f : ContinuousMultilinearMap A M₁ M₂) (c : R') (m : ∀ i, M₁ i) : - (c • f) m = c • f m := - rfl +instance : IsSMulApply R' (ContinuousMultilinearMap A M₁ M₂) (∀ i, M₁ i) M₂ where + smul_apply _ _ _ := rfl + +@[deprecated (since := "2026-06-10")] protected alias smul_apply := smul_apply @[simp] theorem toMultilinearMap_smul (c : R') (f : ContinuousMultilinearMap A M₁ M₂) : @@ -154,15 +155,13 @@ theorem toMultilinearMap_smul (c : R') (f : ContinuousMultilinearMap A M₁ M₂ rfl instance [SMulCommClass R' R'' M₂] : SMulCommClass R' R'' (ContinuousMultilinearMap A M₁ M₂) := - ⟨fun _ _ _ => ext fun _ => smul_comm _ _ _⟩ + FunLike.smulCommClass instance [SMul R' R''] [IsScalarTower R' R'' M₂] : - IsScalarTower R' R'' (ContinuousMultilinearMap A M₁ M₂) := - ⟨fun _ _ _ => ext fun _ => smul_assoc _ _ _⟩ + IsScalarTower R' R'' (ContinuousMultilinearMap A M₁ M₂) := FunLike.isScalarTower instance [DistribSMul R'ᵐᵒᵖ M₂] [IsCentralScalar R' M₂] : - IsCentralScalar R' (ContinuousMultilinearMap A M₁ M₂) := - ⟨fun _ _ => ext fun _ => op_smul_eq_smul _ _⟩ + IsCentralScalar R' (ContinuousMultilinearMap A M₁ M₂) := FunLike.isCentralScalar end SMul @@ -183,9 +182,10 @@ variable [ContinuousAdd M₂] instance : Add (ContinuousMultilinearMap R M₁ M₂) := ⟨fun f f' => ⟨f.toMultilinearMap + f'.toMultilinearMap, f.cont.add f'.cont⟩⟩ -@[simp] -theorem add_apply (m : ∀ i, M₁ i) : (f + f') m = f m + f' m := - rfl +instance : IsAddApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where + add_apply _ _ _ := rfl + +@[deprecated (since := "2026-06-10")] protected alias add_apply := add_apply @[simp] theorem toMultilinearMap_add (f g : ContinuousMultilinearMap R M₁ M₂) : @@ -205,10 +205,7 @@ def applyAddHom (m : ∀ i, M₁ i) : ContinuousMultilinearMap R M₁ M₂ →+ map_zero' := rfl map_add' _ _ := rfl -@[simp] -theorem sum_apply {α : Type*} (f : α → ContinuousMultilinearMap R M₁ M₂) (m : ∀ i, M₁ i) - {s : Finset α} : (∑ a ∈ s, f a) m = ∑ a ∈ s, f a m := - map_sum (applyAddHom m) f s +@[deprecated (since := "2026-06-10")] protected alias sum_apply := sum_apply end ContinuousAdd @@ -482,16 +479,18 @@ variable [IsTopologicalAddGroup M₂] instance : Neg (ContinuousMultilinearMap R M₁ M₂) := ⟨fun f => { -f.toMultilinearMap with cont := f.cont.neg }⟩ -@[simp] -theorem neg_apply (m : ∀ i, M₁ i) : (-f) m = -f m := - rfl +instance : IsNegApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where + neg_apply _ _ := rfl + +@[deprecated (since := "2026-06-10")] protected alias neg_apply := neg_apply instance : Sub (ContinuousMultilinearMap R M₁ M₂) := ⟨fun f g => { f.toMultilinearMap - g.toMultilinearMap with cont := f.cont.sub g.cont }⟩ -@[simp] -theorem sub_apply (m : ∀ i, M₁ i) : (f - f') m = f m - f' m := - rfl +instance : IsSubApply (ContinuousMultilinearMap R M₁ M₂) (∀ i, M₁ i) M₂ where + sub_apply _ _ _ := rfl + +@[deprecated (since := "2026-06-10")] protected alias sub_apply := sub_apply instance : AddCommGroup (ContinuousMultilinearMap R M₁ M₂) := fast_instance% toMultilinearMap_injective.addCommGroup _ rfl (fun _ _ => rfl) (fun _ => rfl) (fun _ _ => rfl) @@ -686,7 +685,7 @@ theorem mkPiRing_eq_iff {z₁ z₂ : M} : exact MultilinearMap.mkPiRing_eq_iff theorem mkPiRing_zero : ContinuousMultilinearMap.mkPiRing R ι (0 : M) = 0 := by - ext; rw [mkPiRing_apply, smul_zero, ContinuousMultilinearMap.zero_apply] + ext; rw [mkPiRing_apply, smul_zero, zero_apply] theorem mkPiRing_eq_zero_iff (z : M) : ContinuousMultilinearMap.mkPiRing R ι z = 0 ↔ z = 0 := by rw [← mkPiRing_zero, mkPiRing_eq_iff] diff --git a/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean b/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean index 00a041c4b2d9f4..e75d37c8867b41 100644 --- a/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean +++ b/Mathlib/Topology/Algebra/Module/Spaces/WeakBilin.lean @@ -128,7 +128,7 @@ set_option backward.isDefEq.respectTransparency false in /-- Scalar multiplication by `𝕜` on `WeakBilin B` is continuous. -/ instance instContinuousSMul [ContinuousSMul 𝕜 𝕜] : ContinuousSMul 𝕜 (WeakBilin B) := by refine ⟨continuous_induced_rng.2 ?_⟩ - refine cast (congr_arg _ ?_) (continuous_fst.smul ((coeFn_continuous B).comp continuous_snd)) + refine cast (congr_arg _ ?_) (continuous_fst.fun_smul ((coeFn_continuous B).comp continuous_snd)) ext simp only [Function.comp_apply, Pi.smul_apply, map_smulₛₗ, RingHom.id_apply, LinearMap.smul_apply] diff --git a/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean b/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean index fc6dcabebd8be6..e7ac7aa3da5de1 100644 --- a/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean +++ b/Mathlib/Topology/Algebra/Module/Spaces/WeakDual.lean @@ -137,6 +137,25 @@ equivalence `StrongDual.toWeakDual` in the other direction. -/ def toStrongDual : WeakDual 𝕜 E ≃ₗ[𝕜] StrongDual 𝕜 E := StrongDual.toWeakDual.symm +@[simp] +theorem symm_toStrongDual : + (toStrongDual (𝕜 := 𝕜) (E := E)).symm = StrongDual.toWeakDual := + rfl + +@[simp] +theorem _root_.StrongDual.symm_toWeakDual : + (StrongDual.toWeakDual (𝕜 := 𝕜) (E := E)).symm = toStrongDual := + rfl + +@[simp] +theorem _root_.StrongDual.toStrongDual_toWeakDual (x : StrongDual 𝕜 E) : + x.toWeakDual.toStrongDual = x := + rfl + +@[simp] +theorem toWeakDual_toStrongDual (x : WeakDual 𝕜 E) : x.toStrongDual.toWeakDual = x := + rfl + @[simp] theorem toStrongDual_apply (x : WeakDual 𝕜 E) (y : E) : (toStrongDual x) y = x y := rfl diff --git a/Mathlib/Topology/Algebra/Monoid.lean b/Mathlib/Topology/Algebra/Monoid.lean index 1f2501628fd2d6..b175611ea822d6 100644 --- a/Mathlib/Topology/Algebra/Monoid.lean +++ b/Mathlib/Topology/Algebra/Monoid.lean @@ -809,8 +809,8 @@ instance AddMonoid.continuousSMul_nat {A} [AddMonoid A] [TopologicalSpace A] -- To properly fix this, we should make sure that `continuity` applies its -- lemmas with reducible transparency, preventing the unfolding of `^`. But this -- is quite an invasive change. -@[to_additive (attr := aesop safe -100 (rule_sets := [Continuous]), fun_prop)] -theorem Continuous.pow {f : X → M} (h : Continuous f) (n : ℕ) : Continuous fun b => f b ^ n := +@[to_fun (attr := to_additive (attr := aesop safe -100 (rule_sets := [Continuous]), fun_prop))] +theorem Continuous.pow {f : X → M} (h : Continuous f) (n : ℕ) : Continuous (f ^ n) := (continuous_pow n).comp h @[to_additive] @@ -826,19 +826,19 @@ theorem Filter.Tendsto.pow {l : Filter α} {f : α → M} {x : M} (hf : Tendsto Tendsto (fun x => f x ^ n) l (𝓝 (x ^ n)) := (continuousAt_pow _ _).tendsto.comp hf -@[to_additive] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousWithinAt.pow {f : X → M} {x : X} {s : Set X} (hf : ContinuousWithinAt f s x) - (n : ℕ) : ContinuousWithinAt (fun x => f x ^ n) s x := + (n : ℕ) : ContinuousWithinAt (f ^ n) s x := Filter.Tendsto.pow hf n -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousAt.pow {f : X → M} {x : X} (hf : ContinuousAt f x) (n : ℕ) : - ContinuousAt (fun x => f x ^ n) x := + ContinuousAt (f ^ n) x := Filter.Tendsto.pow hf n -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousOn.pow {f : X → M} {s : Set X} (hf : ContinuousOn f s) (n : ℕ) : - ContinuousOn (fun x => f x ^ n) s := fun x hx => (hf x hx).pow n + ContinuousOn (f ^ n) s := fun x hx => (hf x hx).pow n /-- If `R` acts on `A` via `A`, then continuous multiplication implies continuous scalar multiplication by constants. diff --git a/Mathlib/Topology/Algebra/MulAction.lean b/Mathlib/Topology/Algebra/MulAction.lean index 04f549af6779c9..0ec40fe17fabd7 100644 --- a/Mathlib/Topology/Algebra/MulAction.lean +++ b/Mathlib/Topology/Algebra/MulAction.lean @@ -117,22 +117,22 @@ theorem Filter.Tendsto.smul_const {f : α → M} {l : Filter α} {c : M} (hf : T variable {f : Y → M} {g : Y → X} {b : Y} {s : Set Y} -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousWithinAt.smul (hf : ContinuousWithinAt f s b) (hg : ContinuousWithinAt g s b) : - ContinuousWithinAt (fun x => f x • g x) s b := + ContinuousWithinAt (f • g) s b := Filter.Tendsto.smul hf hg -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousAt.smul (hf : ContinuousAt f b) (hg : ContinuousAt g b) : - ContinuousAt (fun x => f x • g x) b := + ContinuousAt (f • g) b := Filter.Tendsto.smul hf hg -@[to_additive (attr := fun_prop)] +@[to_fun (attr := to_additive (attr := fun_prop))] theorem ContinuousOn.smul (hf : ContinuousOn f s) (hg : ContinuousOn g s) : - ContinuousOn (fun x => f x • g x) s := fun x hx => (hf x hx).smul (hg x hx) + ContinuousOn (f • g) s := fun x hx => (hf x hx).smul (hg x hx) -@[to_additive (attr := continuity, fun_prop)] -theorem Continuous.smul (hf : Continuous f) (hg : Continuous g) : Continuous fun x => f x • g x := +@[to_fun (attr := to_additive (attr := continuity, fun_prop))] +theorem Continuous.smul (hf : Continuous f) (hg : Continuous g) : Continuous (f • g) := continuous_smul.comp (hf.prodMk hg) /-- If a scalar action is central, then its right action is continuous when its left action is. -/ @@ -191,7 +191,7 @@ lemma Topology.IsInducing.continuousSMul {N : Type*} [SMul N Y] [TopologicalSpac ContinuousSMul N Y where continuous_smul := by simpa only [hg.continuous_iff, Function.comp_def, hsmul] - using (hf.comp continuous_fst).smul <| hg.continuous.comp continuous_snd + using (hf.comp continuous_fst).fun_smul <| hg.continuous.comp continuous_snd @[to_additive] instance SMulMemClass.continuousSMul {S : Type*} [SetLike S X] [SMulMemClass S M X] (s : S) : diff --git a/Mathlib/Topology/Algebra/Order/Floor.lean b/Mathlib/Topology/Algebra/Order/Floor.lean index fca55d68eb8cb1..5f59c6fa74b19c 100644 --- a/Mathlib/Topology/Algebra/Order/Floor.lean +++ b/Mathlib/Topology/Algebra/Order/Floor.lean @@ -86,7 +86,7 @@ theorem continuousOn_floor (n : ℤ) : ContinuousOn (fun x => floor x : α → α) (Ico n (n + 1) : Set α) := (continuousOn_congr <| floor_eq_on_Ico' n).mpr continuousOn_const -theorem continuousOn_ceil [IsStrictOrderedRing α] (n : ℤ) : +theorem continuousOn_ceil (n : ℤ) : ContinuousOn (fun x => ceil x : α → α) (Ioc (n - 1) n : Set α) := (continuousOn_congr <| ceil_eq_on_Ioc' n).mpr continuousOn_const diff --git a/Mathlib/Topology/Algebra/PontryaginDual.lean b/Mathlib/Topology/Algebra/PontryaginDual.lean index 769f32e134617a..2dd281ea6672e0 100644 --- a/Mathlib/Topology/Algebra/PontryaginDual.lean +++ b/Mathlib/Topology/Algebra/PontryaginDual.lean @@ -61,10 +61,6 @@ namespace PontryaginDual open ContinuousMonoidHom -#adaptation_note /-- nightly-2026-03-31 -This `set_option` is necessary because of a compiler bug. --/ -set_option backward.inferInstanceAs.wrap.data false in instance : CommGroup (PontryaginDual A) := inferInstanceAs (CommGroup (A →ₜ* Circle)) deriving instance diff --git a/Mathlib/Topology/Algebra/UniformMulAction.lean b/Mathlib/Topology/Algebra/UniformMulAction.lean index 99c5fecdd7ece9..a5969d94f8b291 100644 --- a/Mathlib/Topology/Algebra/UniformMulAction.lean +++ b/Mathlib/Topology/Algebra/UniformMulAction.lean @@ -238,7 +238,7 @@ noncomputable instance [Monoid M] [MulAction M X] [UniformContinuousConstSMul M MulAction M (Completion X) where one_smul := ext' (continuous_const_smul _) continuous_id fun a => by rw [← coe_smul, one_smul] mul_smul x y := - ext' (continuous_const_smul _) ((continuous_const_smul _).const_smul _) fun a => by + ext' (continuous_const_smul _) ((continuous_const_smul _).fun_const_smul _) fun a => by simp only [← coe_smul, mul_smul] end Completion diff --git a/Mathlib/Topology/Algebra/Valued/WithVal.lean b/Mathlib/Topology/Algebra/Valued/WithVal.lean index 6cd812ef591d80..52af5959ca41f2 100644 --- a/Mathlib/Topology/Algebra/Valued/WithVal.lean +++ b/Mathlib/Topology/Algebra/Valued/WithVal.lean @@ -264,7 +264,7 @@ instance {P : Type*} [Ring S] [SMul P S] [SMul R S] [SMul P R] instance {P : Type*} [Ring S] [SMul P R] [SMul S R] [SMul P S] [IsScalarTower P S R] (v : Valuation S Γ₀) : IsScalarTower P (WithVal v) R where - smul_assoc := by simp [smul_right_def, smul_left_def, - toVal_smul] + smul_assoc := by simp [smul_right_def, smul_left_def, -toVal_smul] instance [AddCommMonoid S] [Module R S] : Module (WithVal v) S := .compHom S (equiv v).toRingHom diff --git a/Mathlib/Topology/Algebra/WithZeroTopology.lean b/Mathlib/Topology/Algebra/WithZeroTopology.lean index 1fdd828d8d99c0..760e23fee1833b 100644 --- a/Mathlib/Topology/Algebra/WithZeroTopology.lean +++ b/Mathlib/Topology/Algebra/WithZeroTopology.lean @@ -140,7 +140,6 @@ theorem isOpen_Iio {a : Γ₀} : IsOpen (Iio a) := /-- The topology on a linearly ordered group with zero element adjoined is compatible with the order structure: the set `{p : Γ₀ × Γ₀ | p.1 ≤ p.2}` is closed. -/ -@[nolint defLemma] scoped instance (priority := 100) orderClosedTopology : OrderClosedTopology Γ₀ where isClosed_le' := by simp only [← isOpen_compl_iff, compl_setOf, not_le, isOpen_iff_mem_nhds] @@ -149,7 +148,6 @@ scoped instance (priority := 100) orderClosedTopology : OrderClosedTopology Γ exact Iio_mem_nhds hab /-- The topology on a linearly ordered group with zero element adjoined is T₅. -/ -@[nolint defLemma] scoped instance (priority := 100) t5Space : T5Space Γ₀ where completely_normal := fun s t h₁ h₂ => by by_cases hs : 0 ∈ s @@ -159,7 +157,6 @@ scoped instance (priority := 100) t5Space : T5Space Γ₀ where /-- The topology on a linearly ordered group with zero element adjoined makes it a topological monoid. -/ -@[nolint defLemma] scoped instance (priority := 100) : ContinuousMul Γ₀ where continuous_mul := by simp only [continuous_iff_continuousAt, ContinuousAt] @@ -182,7 +179,6 @@ scoped instance (priority := 100) : ContinuousMul Γ₀ where rw [nhds_prod_eq, nhds_of_ne_zero hx, nhds_of_ne_zero hy, prod_pure_pure] exact pure_le_nhds (x * y) -@[nolint defLemma] scoped instance (priority := 100) : ContinuousInv₀ Γ₀ := ⟨fun γ h => by rw [ContinuousAt, nhds_of_ne_zero h] diff --git a/Mathlib/Topology/CWComplex/Classical/Basic.lean b/Mathlib/Topology/CWComplex/Classical/Basic.lean index 97b4720eaba62a..78eab46d614820 100644 --- a/Mathlib/Topology/CWComplex/Classical/Basic.lean +++ b/Mathlib/Topology/CWComplex/Classical/Basic.lean @@ -289,6 +289,20 @@ lemma RelCWComplex.map_zero_mem_closedCell [RelCWComplex C D] (n : ℕ) (i : cel map n i 0 ∈ closedCell n i := openCell_subset_closedCell _ _ (map_zero_mem_openCell _ _) +lemma RelCWComplex.openCell_nonempty [RelCWComplex C D] (n : ℕ) (j : cell C n) : + (openCell n j).Nonempty := + ⟨(map n j) 0, map_zero_mem_openCell n j⟩ + +lemma RelCWComplex.closedCell_nonempty [RelCWComplex C D] (n : ℕ) (j : cell C n) : + (closedCell n j).Nonempty := + ⟨(map n j) 0, map_zero_mem_closedCell n j⟩ + +/-- If two open cells are equal, so are the underlying cells. -/ +lemma RelCWComplex.openCell_congr [RelCWComplex C D] (n : ℕ) {s t : cell C n} + (st : openCell n s = openCell n t) : s = t := by + contrapose! st + exact (disjoint_openCell_of_ne (by simpa)).ne (openCell_nonempty n s).ne_empty + /-- This is an auxiliary lemma used to prove `RelCWComplex.eq_of_eq_union_iUnion`. -/ private lemma RelCWComplex.subset_of_eq_union_iUnion [RelCWComplex C D] (I J : Π n, Set (cell C n)) (hIJ : D ∪ ⋃ (n : ℕ) (j : I n), openCell (C := C) n j = @@ -1061,4 +1075,45 @@ lemma RelCWComplex.disjoint_interior_base_iUnion_closedCell [T2Space X] [RelCWCo simp_rw [disjoint_iff_inter_eq_empty, inter_iUnion, disjoint_interior_base_closedCell.inter_eq, iUnion_empty] +/-- A closed discrete subset of a space is a CW complex. -/ +@[reducible, simps -isSimp] +def CWComplex.OfDiscreteClosed (hD : IsDiscrete D) (Dc : IsClosed D) : CWComplex D where + cell n := match n with + | 0 => D + | (_ + 1) => PEmpty + map n i := match n with + | 0 => PartialEquiv.single ![] i + | (_ + 1) => i.elim + source_eq n i := match n with + | 0 => by simp [ball, Matrix.empty_eq, eq_univ_iff_forall] + | (_ + 1) => i.elim + continuousOn n i := match n with + | 0 => continuousOn_const + | (_ + 1) => i.elim + continuousOn_symm n i := match n with + | 0 => continuousOn_const + | (_ + 1) => i.elim + pairwiseDisjoint' := by + simp_rw [PairwiseDisjoint, Set.Pairwise, Function.onFun] + rintro ⟨_|n, j⟩ _ ⟨_|m, i⟩ _ ne + · simp_all [Subtype.coe_injective.ne] + · exact i.elim + · tauto + · exact i.elim + mapsTo' n i := match n with + | 0 => by simp [Matrix.zero_empty, sphere_eq_empty_of_subsingleton] + | (_ + 1) => i.elim + closed' A AD _ := isClosed_of_subset_discrete_closed AD hD Dc + union' := by + apply subset_antisymm (iUnion₂_subset_iff.mpr fun n ↦ by cases n <;> simp) + intro x xD + simp only [mem_iUnion, mem_image, mem_closedBall, dist_zero_right] + refine ⟨0, ?_⟩ + simpa [-Matrix.zero_empty] + +/-- A discrete space is a CW complex. -/ +instance CWComplex.ofDiscreteTopology {X : Type*} [TopologicalSpace X] [DiscreteTopology X] : + CWComplex (univ : Set X) := + CWComplex.OfDiscreteClosed IsDiscrete.univ isClosed_univ + end Topology diff --git a/Mathlib/Topology/Category/CompHausLike/Limits.lean b/Mathlib/Topology/Category/CompHausLike/Limits.lean index 48241d88790508..eedc1febd64392 100644 --- a/Mathlib/Topology/Category/CompHausLike/Limits.lean +++ b/Mathlib/Topology/Category/CompHausLike/Limits.lean @@ -120,6 +120,12 @@ lemma finiteCoproduct.ι_desc_apply {B : CompHausLike P} {π : (a : α) → X a instance : HasCoproduct X where exists_colimit := ⟨finiteCoproduct.cofan X, finiteCoproduct.isColimit X⟩ +/- +This linter complains that the universes `u` and `w` only occur together, but `w` appears by itself +in the indexing type of the coproduct. In almost all cases, `w` will be either `0` or `u`, but we +want to allow both possibilities. +-/ +set_option linter.checkUnivs false in variable (P) in /-- A typeclass describing the property that forming all finite disjoint unions is stable under the @@ -128,13 +134,6 @@ property `P`. class HasExplicitFiniteCoproducts : Prop where hasProp {α : Type w} [Finite α] (X : α → CompHausLike.{max u w} P) : HasExplicitFiniteCoproduct X -/- -This linter complains that the universes `u` and `w` only occur together, but `w` appears by itself -in the indexing type of the coproduct. In almost all cases, `w` will be either `0` or `u`, but we -want to allow both possibilities. --/ -attribute [nolint checkUnivs] HasExplicitFiniteCoproducts - attribute [instance] HasExplicitFiniteCoproducts.hasProp instance [HasExplicitFiniteCoproducts.{w} P] (α : Type w) [Finite α] : diff --git a/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean b/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean index 4de1c96a203ce1..cf9d1ffe5faa15 100644 --- a/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean +++ b/Mathlib/Topology/Category/TopCat/GrothendieckTopology.lean @@ -66,9 +66,7 @@ def precoverage : Precoverage TopCat.{u} := Types.jointlySurjectivePrecoverage.comap (forget TopCat) ⊓ isOpenEmbedding.precoverage deriving Precoverage.HasIsos, Precoverage.IsStableUnderComposition -#adaptation_note /-- nightly-2026-03-04: Strange we need `noncomputable` for a `Prop` instance. -Will be fixed by https://github.com/leanprover/lean4/pull/12789 -/ -deriving noncomputable instance Precoverage.IsStableUnderBaseChange for precoverage +deriving instance Precoverage.IsStableUnderBaseChange for precoverage /-- The Grothendieck topology on the category of topological spaces is the topology given by jointly surjective open embeddings. -/ diff --git a/Mathlib/Topology/Category/TopCat/Opens.lean b/Mathlib/Topology/Category/TopCat/Opens.lean index 24e13f01622724..96f6645978055a 100644 --- a/Mathlib/Topology/Category/TopCat/Opens.lean +++ b/Mathlib/Topology/Category/TopCat/Opens.lean @@ -7,6 +7,8 @@ module public import Mathlib.CategoryTheory.Category.GaloisConnection public import Mathlib.CategoryTheory.EqToHom +public import Mathlib.CategoryTheory.Limits.Preorder +public import Mathlib.CategoryTheory.Limits.Preserves.Shapes.Products public import Mathlib.Topology.Category.TopCat.EpiMono public import Mathlib.Topology.Sets.Opens @@ -341,6 +343,15 @@ lemma Topology.IsOpenEmbedding.functor_obj_injective {X Y : TopCat.{u}} {f : X (hf : IsOpenEmbedding f) : Function.Injective hf.functor.obj := fun _ _ e ↦ Opens.ext (Set.image_injective.mpr hf.injective (congr_arg (↑· : Opens Y → Set Y) e)) +lemma Topology.IsOpenEmbedding.functor_obj_iInf {X Y : TopCat.{u}} (f : X ⟶ Y) + (hf : Topology.IsOpenEmbedding f) {ι : Type*} [Nonempty ι] [Finite ι] + (g : ι → TopologicalSpace.Opens X) : + hf.functor.obj (⨅ i, g i) = ⨅ i, hf.functor.obj (g i) := by + ext : 1 + simp only [IsOpenMap.coe_functor_obj, TopologicalSpace.Opens.coe_iInf] + rw [Set.InjOn.image_iInter_eq] + exact hf.injective.injOn + namespace Topology.IsInducing /-- Given an inducing map `X ⟶ Y` and some `U : Opens X`, this is the union of all open sets @@ -463,4 +474,15 @@ theorem adjunction_counit_map_functor {X : TopCat.{u}} {U : Opens X} (V : Opens eqToHom (by dsimp; rw [map_functor_eq V]) := by subsingleton +open Limits in +instance {X Y : TopCat.{u}} (f : X ⟶ Y) (hf : Topology.IsOpenEmbedding f) {ι : Type*} + [Nonempty ι] [Finite ι] : + PreservesLimitsOfShape (Discrete ι) hf.functor := by + apply +allowSynthFailures preservesLimitsOfShape_of_discrete + intro g + refine preservesLimit_of_preserves_limit_cone (Preorder.isLimitIInf g) ?_ + refine (Limits.Fan.isLimitMapConeEquiv _ _ _).symm (Preorder.isLimitOfIsGLB _ _ ?_) + simp only [Discrete.range_functor, homOfLE_leOfHom, Fan.mk_pt, hf.functor_obj_iInf] + apply isGLB_iInf + end TopologicalSpace.Opens diff --git a/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean b/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean index d06465c472ce81..abcccc287c9198 100644 --- a/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean +++ b/Mathlib/Topology/Compactness/DeltaGeneratedSpace.lean @@ -49,10 +49,10 @@ abbrev of : Type _ := WithGeneratedByTopology (fun n ↦ Fin n → ℝ) Y /-- Delta-generated spaces are locally path-connected. -/ instance [DeltaGeneratedSpace X] : - LocPathConnectedSpace X := by + LocallyPathConnectedSpace X := by rw [← IsGeneratedBy.generatedBy_eq (X := fun n ↦ Fin n → ℝ) (Y := X), generatedBy_eq_coinduced] - exact LocPathConnectedSpace.coinduced _ + exact LocallyPathConnectedSpace.coinduced _ /-- Delta-generated spaces are sequential. -/ instance [DeltaGeneratedSpace X] : SequentialSpace X := by diff --git a/Mathlib/Topology/Compactness/HilbertCubeEmbedding.lean b/Mathlib/Topology/Compactness/HilbertCubeEmbedding.lean deleted file mode 100644 index b3558a83cc2ad7..00000000000000 --- a/Mathlib/Topology/Compactness/HilbertCubeEmbedding.lean +++ /dev/null @@ -1,8 +0,0 @@ -module -- shake: keep-all - -import Mathlib.Analysis.Normed.Group.Basic -import Mathlib.Data.EReal.Inv -import Mathlib.Topology.Algebra.InfiniteSum.Order -import Mathlib.Topology.MetricSpace.Bounded - -deprecated_module (since := "2025-12-18") diff --git a/Mathlib/Topology/Compactness/PseudometrizableLindelof.lean b/Mathlib/Topology/Compactness/PseudometrizableLindelof.lean deleted file mode 100644 index 571e1749539bd4..00000000000000 --- a/Mathlib/Topology/Compactness/PseudometrizableLindelof.lean +++ /dev/null @@ -1,15 +0,0 @@ -/- -Copyright (c) 2023 Josha Dekker. All rights reserved. -Released under Apache 2.0 license as described in the file LICENSE. -Authors: Josha Dekker --/ -module -- shake: keep-all - -public import Mathlib.Data.Finset.Attr -public import Mathlib.Tactic.Common -public import Mathlib.Tactic.Continuity -public import Mathlib.Tactic.Finiteness.Attr -public import Mathlib.Tactic.SetLike -public import Mathlib.Util.CompileInductive - -deprecated_module (since := "2025-12-10") diff --git a/Mathlib/Topology/Connected/LocPathConnected.lean b/Mathlib/Topology/Connected/LocPathConnected.lean index 4f43cadb842f43..cc71984b49b2fd 100644 --- a/Mathlib/Topology/Connected/LocPathConnected.lean +++ b/Mathlib/Topology/Connected/LocPathConnected.lean @@ -3,274 +3,8 @@ Copyright (c) 2020 Patrick Massot. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Patrick Massot, Ben Eltschig -/ -module +module -- shake: keep-all -public import Mathlib.Topology.Connected.PathConnected -public import Mathlib.Topology.AlexandrovDiscrete +public import Mathlib.Topology.Connected.LocallyPathConnected -/-! -# Locally path-connected spaces - -This file defines `LocPathConnectedSpace X`, a predicate class asserting that `X` is locally -path-connected, in that each point has a basis of path-connected neighborhoods. - -## Main results - -* `IsOpen.pathComponent` / `IsClosed.pathComponent`: in locally path-connected spaces, - path-components are both open and closed. -* `pathComponent_eq_connectedComponent`: in locally path-connected spaces, path-components and - connected components agree. -* `pathConnectedSpace_iff_connectedSpace`: locally path-connected spaces are path-connected iff they - are connected. -* `instLocallyConnectedSpace`: locally path-connected spaces are also locally connected. -* `IsOpen.locPathConnectedSpace`: open subsets of locally path-connected spaces are - locally path-connected. -* `LocPathConnectedSpace.coinduced` / `Quotient.locPathConnectedSpace`: quotients of locally - path-connected spaces are locally path-connected. -* `Sum.locPathConnectedSpace` / `Sigma.locPathConnectedSpace`: disjoint unions of locally - path-connected spaces are locally path-connected. - -Abstractly, this also shows that locally path-connected spaces form a coreflective subcategory of -the category of topological spaces, although we do not prove that in this form here. - -## Implementation notes - -In the definition of `LocPathConnectedSpace X` we require neighbourhoods in the basis to be -path-connected, but not necessarily open; that they can also be required to be open is shown as -a theorem in `isOpen_isPathConnected_basis`. --/ - -@[expose] public section - -noncomputable section - -open Topology Filter unitInterval Set Function - -variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] {x y z : X} {ι : Type*} {F : Set X} - -section LocPathConnectedSpace - -/-- A topological space is locally path connected if, at every point, path connected -neighborhoods form a neighborhood basis. -/ -class LocPathConnectedSpace (X : Type*) [TopologicalSpace X] : Prop where - /-- Each neighborhood filter has a basis of path-connected neighborhoods. -/ - path_connected_basis : ∀ x : X, (𝓝 x).HasBasis (fun s : Set X => s ∈ 𝓝 x ∧ IsPathConnected s) id - -export LocPathConnectedSpace (path_connected_basis) - -theorem LocPathConnectedSpace.of_bases {p : X → ι → Prop} {s : X → ι → Set X} - (h : ∀ x, (𝓝 x).HasBasis (p x) (s x)) (h' : ∀ x i, p x i → IsPathConnected (s x i)) : - LocPathConnectedSpace X where - path_connected_basis x := by - rw [hasBasis_self] - intro t ht - rcases (h x).mem_iff.mp ht with ⟨i, hpi, hi⟩ - exact ⟨s x i, (h x).mem_of_mem hpi, h' x i hpi, hi⟩ - -variable [LocPathConnectedSpace X] - -protected theorem IsOpen.pathComponentIn (hF : IsOpen F) (x : X) : - IsOpen (pathComponentIn F x) := by - rw [isOpen_iff_mem_nhds] - intro y hy - let ⟨s, hs⟩ := (path_connected_basis y).mem_iff.mp (hF.mem_nhds (pathComponentIn_subset hy)) - exact mem_of_superset hs.1.1 <| pathComponentIn_congr hy ▸ - hs.1.2.subset_pathComponentIn (mem_of_mem_nhds hs.1.1) hs.2 - -/-- In a locally path connected space, each path component is an open set. -/ -protected theorem IsOpen.pathComponent (x : X) : IsOpen (pathComponent x) := by - rw [← pathComponentIn_univ] - exact isOpen_univ.pathComponentIn _ - -/-- In a locally path connected space, each path component is a closed set. -/ -protected theorem IsClosed.pathComponent (x : X) : IsClosed (pathComponent x) := by - rw [← isOpen_compl_iff, isOpen_iff_mem_nhds] - intro y hxy - rcases (path_connected_basis y).ex_mem with ⟨V, hVy, hVc⟩ - filter_upwards [hVy] with z hz hxz - exact hxy <| hxz.trans (hVc.joinedIn _ hz _ (mem_of_mem_nhds hVy)).joined - -/-- In a locally path connected space, each path component is a clopen set. -/ -protected theorem IsClopen.pathComponent (x : X) : IsClopen (pathComponent x) := - ⟨.pathComponent x, .pathComponent x⟩ - -lemma pathComponentIn_mem_nhds (hF : F ∈ 𝓝 x) : pathComponentIn F x ∈ 𝓝 x := by - let ⟨u, huF, hu, hxu⟩ := mem_nhds_iff.mp hF - exact mem_nhds_iff.mpr ⟨pathComponentIn u x, pathComponentIn_mono huF, - hu.pathComponentIn x, mem_pathComponentIn_self hxu⟩ - -theorem PathConnectedSpace.of_locPathConnectedSpace [ConnectedSpace X] : PathConnectedSpace X := - ⟨inferInstance, by simp [← mem_pathComponent_iff, IsClopen.pathComponent _ |>.eq_univ]⟩ - -theorem pathConnectedSpace_iff_connectedSpace : PathConnectedSpace X ↔ ConnectedSpace X := - ⟨fun _ ↦ inferInstance, fun _ ↦ .of_locPathConnectedSpace⟩ - -theorem pathComponent_eq_connectedComponent (x : X) : pathComponent x = connectedComponent x := - (pathComponent_subset_component x).antisymm <| - (IsClopen.pathComponent x).connectedComponent_subset (mem_pathComponent_self _) - -theorem connectedComponent_eq_iff_joined (x y : X) : - connectedComponent x = connectedComponent y ↔ Joined x y := by - rw [← mem_pathComponent_iff, pathComponent_eq_connectedComponent, eq_comm] - exact connectedComponent_eq_iff_mem - -theorem connectedComponentSetoid_eq_pathSetoid : connectedComponentSetoid X = pathSetoid X := - Setoid.ext connectedComponent_eq_iff_joined - -/-- In a locally path-connected space, connected components and path-connected components align -/ -def connectedComponentsEquivZerothHomotopy : ConnectedComponents X ≃ ZerothHomotopy X where - toFun := Quotient.map id (connectedComponent_eq_iff_joined · · |>.mp ·) - invFun := ZerothHomotopy.toConnectedComponents - left_inv := Quot.ind <| congrFun rfl - right_inv := Quot.ind <| congrFun rfl - -@[simp] -lemma connectedComponentsEquivZerothHomotopy_apply (x : X) : - connectedComponentsEquivZerothHomotopy ⟦x⟧ = (.mk x) := - rfl - -@[simp] -lemma coe_connectedComponentsEquivZerothHomotopy_symm : - ⇑connectedComponentsEquivZerothHomotopy.symm = ZerothHomotopy.toConnectedComponents (X := X) := - rfl - -lemma connectedComponentsEquivZerothHomotopy_symm_apply (x : X) : - connectedComponentsEquivZerothHomotopy.symm (.mk x) = ⟦x⟧ := - rfl - -theorem pathConnected_subset_basis {U : Set X} (h : IsOpen U) (hx : x ∈ U) : - (𝓝 x).HasBasis (fun s : Set X => s ∈ 𝓝 x ∧ IsPathConnected s ∧ s ⊆ U) id := - (path_connected_basis x).hasBasis_self_subset (IsOpen.mem_nhds h hx) - -theorem isOpen_isPathConnected_basis (x : X) : - (𝓝 x).HasBasis (fun s : Set X ↦ IsOpen s ∧ x ∈ s ∧ IsPathConnected s) id := by - refine ⟨fun s ↦ ⟨fun hs ↦ ?_, fun ⟨u, hu⟩ ↦ mem_nhds_iff.mpr ⟨u, hu.2, hu.1.1, hu.1.2.1⟩⟩⟩ - have ⟨u, hus, hu, hxu⟩ := mem_nhds_iff.mp hs - exact ⟨pathComponentIn u x, ⟨hu.pathComponentIn _, ⟨mem_pathComponentIn_self hxu, - isPathConnected_pathComponentIn hxu⟩⟩, pathComponentIn_subset.trans hus⟩ - -theorem Topology.IsOpenEmbedding.locPathConnectedSpace {e : Y → X} (he : IsOpenEmbedding e) : - LocPathConnectedSpace Y := - have (y : Y) : - (𝓝 y).HasBasis (fun s ↦ s ∈ 𝓝 (e y) ∧ IsPathConnected s ∧ s ⊆ range e) (e ⁻¹' ·) := - he.basis_nhds <| pathConnected_subset_basis he.isOpen_range (mem_range_self _) - .of_bases this fun x s ⟨_, hs, hse⟩ ↦ by - rwa [he.isPathConnected_iff, image_preimage_eq_of_subset hse] - -theorem IsOpen.locPathConnectedSpace {U : Set X} (h : IsOpen U) : LocPathConnectedSpace U := - h.isOpenEmbedding_subtypeVal.locPathConnectedSpace - -theorem IsOpen.isConnected_iff_isPathConnected {U : Set X} (U_op : IsOpen U) : - IsConnected U ↔ IsPathConnected U := by - rw [isConnected_iff_connectedSpace, isPathConnected_iff_pathConnectedSpace] - haveI := U_op.locPathConnectedSpace - exact pathConnectedSpace_iff_connectedSpace.symm - -/-- Locally path-connected spaces are locally connected. -/ -instance : LocallyConnectedSpace X := by - refine ⟨forall_imp (fun x h ↦ ⟨fun s ↦ ?_⟩) isOpen_isPathConnected_basis⟩ - refine ⟨fun hs ↦ ?_, fun ⟨u, ⟨hu, hxu, _⟩, hus⟩ ↦ mem_nhds_iff.mpr ⟨u, hus, hu, hxu⟩⟩ - let ⟨u, ⟨hu, hxu, hu'⟩, hus⟩ := (h.mem_iff' s).mp hs - exact ⟨u, ⟨hu, hxu, hu'.isConnected⟩, hus⟩ - -/-- A space is locally path-connected iff all path components of open subsets are open. -/ -lemma locPathConnectedSpace_iff_isOpen_pathComponentIn {X : Type*} [TopologicalSpace X] : - LocPathConnectedSpace X ↔ ∀ (x : X) (u : Set X), IsOpen u → IsOpen (pathComponentIn u x) := - ⟨fun _ _ _ hu ↦ hu.pathComponentIn _, fun h ↦ ⟨fun x ↦ ⟨fun s ↦ by - refine ⟨fun hs ↦ ?_, fun ⟨_, ht⟩ ↦ Filter.mem_of_superset ht.1.1 ht.2⟩ - let ⟨u, hu⟩ := mem_nhds_iff.mp hs - exact ⟨pathComponentIn u x, ⟨(h x u hu.2.1).mem_nhds (mem_pathComponentIn_self hu.2.2), - isPathConnected_pathComponentIn hu.2.2⟩, pathComponentIn_subset.trans hu.1⟩⟩⟩⟩ - -/-- A space is locally path-connected iff all path components of open subsets are neighbourhoods. -/ -lemma locPathConnectedSpace_iff_pathComponentIn_mem_nhds {X : Type*} [TopologicalSpace X] : - LocPathConnectedSpace X ↔ - ∀ x : X, ∀ u : Set X, IsOpen u → x ∈ u → pathComponentIn u x ∈ nhds x := by - rw [locPathConnectedSpace_iff_isOpen_pathComponentIn] - simp_rw [forall_comm (β := Set X), ← imp_forall_iff] - refine forall_congr' fun u ↦ imp_congr_right fun _ ↦ ?_ - exact ⟨fun h x hxu ↦ (h x).mem_nhds (mem_pathComponentIn_self hxu), - fun h x ↦ isOpen_iff_mem_nhds.mpr fun y hy ↦ - pathComponentIn_congr hy ▸ h y <| pathComponentIn_subset hy⟩ - -/-- Any topology coinduced by a locally path-connected topology is locally path-connected. -/ -lemma LocPathConnectedSpace.coinduced {Y : Type*} (f : X → Y) : - @LocPathConnectedSpace Y (.coinduced f ‹_›) := by - let _ := TopologicalSpace.coinduced f ‹_›; have hf : Continuous f := continuous_coinduced_rng - refine locPathConnectedSpace_iff_isOpen_pathComponentIn.mpr fun y u hu ↦ - isOpen_coinduced.mpr <| isOpen_iff_mem_nhds.mpr fun x hx ↦ ?_ - have hx' := preimage_mono pathComponentIn_subset hx - refine mem_nhds_iff.mpr ⟨pathComponentIn (f ⁻¹' u) x, ?_, - (hu.preimage hf).pathComponentIn _, mem_pathComponentIn_self hx'⟩ - rw [← image_subset_iff, ← pathComponentIn_congr hx] - exact ((isPathConnected_pathComponentIn hx').image hf).subset_pathComponentIn - ⟨x, mem_pathComponentIn_self hx', rfl⟩ <| - (image_mono pathComponentIn_subset).trans <| u.image_preimage_subset f - -/-- Quotients of locally path-connected spaces are locally path-connected. -/ -lemma Topology.IsQuotientMap.locPathConnectedSpace {f : X → Y} (h : IsQuotientMap f) : - LocPathConnectedSpace Y := - h.isCoinducing.eq_coinduced ▸ LocPathConnectedSpace.coinduced f - -/-- Quotients of locally path-connected spaces are locally path-connected. -/ -instance Quot.locPathConnectedSpace {r : X → X → Prop} : LocPathConnectedSpace (Quot r) := - isQuotientMap_quot_mk.locPathConnectedSpace - -/-- Quotients of locally path-connected spaces are locally path-connected. -/ -instance Quotient.locPathConnectedSpace {s : Setoid X} : LocPathConnectedSpace (Quotient s) := - isQuotientMap_quotient_mk'.locPathConnectedSpace - -/-- Disjoint unions of locally path-connected spaces are locally path-connected. -/ -instance Sum.locPathConnectedSpace [LocPathConnectedSpace Y] : LocPathConnectedSpace (X ⊕ Y) := by - rw [locPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff] - obtain x | y := x - · refine ⟨Sum.inl '' pathComponentIn (Sum.inl ⁻¹' u) x, ?_, ?_, ?_⟩ - · apply IsPathConnected.subset_pathComponentIn - · exact (isPathConnected_pathComponentIn (by exact hxu)).image continuous_inl - · exact ⟨x, mem_pathComponentIn_self hxu, rfl⟩ - · exact (image_mono pathComponentIn_subset).trans (u.image_preimage_subset _) - · exact isOpenMap_inl _ <| (hu.preimage continuous_inl).pathComponentIn _ - · exact ⟨x, mem_pathComponentIn_self hxu, rfl⟩ - · refine ⟨Sum.inr '' pathComponentIn (Sum.inr ⁻¹' u) y, ?_, ?_, ?_⟩ - · apply IsPathConnected.subset_pathComponentIn - · exact (isPathConnected_pathComponentIn (by exact hxu)).image continuous_inr - · exact ⟨y, mem_pathComponentIn_self hxu, rfl⟩ - · exact (image_mono pathComponentIn_subset).trans (u.image_preimage_subset _) - · exact isOpenMap_inr _ <| (hu.preimage continuous_inr).pathComponentIn _ - · exact ⟨y, mem_pathComponentIn_self hxu, rfl⟩ - -/-- Disjoint unions of locally path-connected spaces are locally path-connected. -/ -instance Sigma.locPathConnectedSpace {X : ι → Type*} - [(i : ι) → TopologicalSpace (X i)] [(i : ι) → LocPathConnectedSpace (X i)] : - LocPathConnectedSpace ((i : ι) × X i) := by - rw [locPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff] - refine ⟨(Sigma.mk x.1) '' pathComponentIn ((Sigma.mk x.1) ⁻¹' u) x.2, ?_, ?_, ?_⟩ - · apply IsPathConnected.subset_pathComponentIn - · exact (isPathConnected_pathComponentIn (by exact hxu)).image continuous_sigmaMk - · exact ⟨x.2, mem_pathComponentIn_self hxu, rfl⟩ - · exact (image_mono pathComponentIn_subset).trans (u.image_preimage_subset _) - · exact isOpenMap_sigmaMk _ <| (hu.preimage continuous_sigmaMk).pathComponentIn _ - · exact ⟨x.2, mem_pathComponentIn_self hxu, rfl⟩ - -instance AlexandrovDiscrete.locPathConnectedSpace [AlexandrovDiscrete X] : - LocPathConnectedSpace X := by - apply LocPathConnectedSpace.of_bases nhds_basis_nhdsKer_singleton - simp only [forall_const, IsPathConnected, mem_nhdsKer_singleton] - intro x - exists x, specializes_rfl - intro y hy - symm - apply hy.joinedIn <;> rewrite [mem_nhdsKer_singleton] <;> [assumption; rfl] - -/-- If a space is locally path-connected, the topology of its path components is discrete. -/ -instance : DiscreteTopology <| ZerothHomotopy X := by - refine discreteTopology_iff_isOpen_singleton.mpr fun c ↦ ?_ - obtain ⟨x, rfl⟩ := ZerothHomotopy.mk_surjective c - rw [← ZerothHomotopy.isQuotientMap_mk.isOpen_preimage] - grind [ZerothHomotopy.preimage_singleton_eq_pathComponent, IsOpen.pathComponent] - -/-- A locally path-connected compact space has finitely many path components. -/ -instance [CompactSpace X] : Finite <| ZerothHomotopy X := - finite_of_compact_of_discrete - -end LocPathConnectedSpace +deprecated_module (since := "2026-06-21") diff --git a/Mathlib/Topology/Connected/LocallyPathConnected.lean b/Mathlib/Topology/Connected/LocallyPathConnected.lean new file mode 100644 index 00000000000000..d26494a7700b2a --- /dev/null +++ b/Mathlib/Topology/Connected/LocallyPathConnected.lean @@ -0,0 +1,326 @@ +/- +Copyright (c) 2020 Patrick Massot. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Patrick Massot, Ben Eltschig +-/ +module + +public import Mathlib.Topology.Connected.PathConnected +public import Mathlib.Topology.AlexandrovDiscrete + +/-! +# Locally path-connected spaces + +This file defines `LocallyPathConnectedSpace X`, a predicate class asserting that `X` is locally +path-connected, in that each point has a basis of path-connected neighborhoods. + +## Main results + +* `IsOpen.pathComponent` / `IsClosed.pathComponent`: in locally path-connected spaces, + path-components are both open and closed. +* `pathComponent_eq_connectedComponent`: in locally path-connected spaces, path-components and + connected components agree. +* `pathConnectedSpace_iff_connectedSpace`: locally path-connected spaces are path-connected iff they + are connected. +* `instLocallyConnectedSpace`: locally path-connected spaces are also locally connected. +* `IsOpen.locallyPathConnectedSpace`: open subsets of locally path-connected spaces are + locally path-connected. +* `LocallyPathConnectedSpace.coinduced` / `Quotient.locallyPathConnectedSpace`: quotients of locally + path-connected spaces are locally path-connected. +* `Sum.locallyPathConnectedSpace` / `Sigma.locallyPathConnectedSpace`: disjoint unions of locally + path-connected spaces are locally path-connected. + +Abstractly, this also shows that locally path-connected spaces form a coreflective subcategory of +the category of topological spaces, although we do not prove that in this form here. + +## Implementation notes + +In the definition of `LocallyPathConnectedSpace X` we require neighbourhoods in the basis to be +path-connected, but not necessarily open; that they can also be required to be open is shown as +a theorem in `isOpen_isPathConnected_basis`. +-/ + +@[expose] public section + +noncomputable section + +open Topology Filter unitInterval Set Function + +variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] {x y z : X} {ι : Type*} {F : Set X} + +section LocallyPathConnectedSpace + +/-- A topological space is locally path connected if, at every point, path connected +neighborhoods form a neighborhood basis. -/ +class LocallyPathConnectedSpace (X : Type*) [TopologicalSpace X] : Prop where + /-- Each neighborhood filter has a basis of path-connected neighborhoods. -/ + path_connected_basis : ∀ x : X, (𝓝 x).HasBasis (fun s : Set X => s ∈ 𝓝 x ∧ IsPathConnected s) id + +@[deprecated (since := "2026-06-21")] alias LocPathConnectedSpace := LocallyPathConnectedSpace +@[deprecated (since := "2026-06-21")] +alias LocPathConnectedSpace.path_connected_basis := + LocallyPathConnectedSpace.path_connected_basis + +export LocallyPathConnectedSpace (path_connected_basis) + +theorem LocallyPathConnectedSpace.of_bases {p : X → ι → Prop} {s : X → ι → Set X} + (h : ∀ x, (𝓝 x).HasBasis (p x) (s x)) (h' : ∀ x i, p x i → IsPathConnected (s x i)) : + LocallyPathConnectedSpace X where + path_connected_basis x := by + rw [hasBasis_self] + intro t ht + rcases (h x).mem_iff.mp ht with ⟨i, hpi, hi⟩ + exact ⟨s x i, (h x).mem_of_mem hpi, h' x i hpi, hi⟩ + +@[deprecated (since := "2026-06-21")] +alias LocPathConnectedSpace.of_bases := LocallyPathConnectedSpace.of_bases + +variable [LocallyPathConnectedSpace X] + +protected theorem IsOpen.pathComponentIn (hF : IsOpen F) (x : X) : + IsOpen (pathComponentIn F x) := by + rw [isOpen_iff_mem_nhds] + intro y hy + let ⟨s, hs⟩ := (path_connected_basis y).mem_iff.mp (hF.mem_nhds (pathComponentIn_subset hy)) + exact mem_of_superset hs.1.1 <| pathComponentIn_congr hy ▸ + hs.1.2.subset_pathComponentIn (mem_of_mem_nhds hs.1.1) hs.2 + +/-- In a locally path connected space, each path component is an open set. -/ +protected theorem IsOpen.pathComponent (x : X) : IsOpen (pathComponent x) := by + rw [← pathComponentIn_univ] + exact isOpen_univ.pathComponentIn _ + +/-- In a locally path connected space, each path component is a closed set. -/ +protected theorem IsClosed.pathComponent (x : X) : IsClosed (pathComponent x) := by + rw [← isOpen_compl_iff, isOpen_iff_mem_nhds] + intro y hxy + rcases (path_connected_basis y).ex_mem with ⟨V, hVy, hVc⟩ + filter_upwards [hVy] with z hz hxz + exact hxy <| hxz.trans (hVc.joinedIn _ hz _ (mem_of_mem_nhds hVy)).joined + +/-- In a locally path connected space, each path component is a clopen set. -/ +protected theorem IsClopen.pathComponent (x : X) : IsClopen (pathComponent x) := + ⟨.pathComponent x, .pathComponent x⟩ + +lemma pathComponentIn_mem_nhds (hF : F ∈ 𝓝 x) : pathComponentIn F x ∈ 𝓝 x := by + let ⟨u, huF, hu, hxu⟩ := mem_nhds_iff.mp hF + exact mem_nhds_iff.mpr ⟨pathComponentIn u x, pathComponentIn_mono huF, + hu.pathComponentIn x, mem_pathComponentIn_self hxu⟩ + +theorem PathConnectedSpace.of_locallyPathConnectedSpace [ConnectedSpace X] : PathConnectedSpace X := + ⟨inferInstance, by simp [← mem_pathComponent_iff, IsClopen.pathComponent _ |>.eq_univ]⟩ + +@[deprecated (since := "2026-06-21")] +alias PathConnectedSpace.of_locPathConnectedSpace := PathConnectedSpace.of_locallyPathConnectedSpace + +theorem pathConnectedSpace_iff_connectedSpace : PathConnectedSpace X ↔ ConnectedSpace X := + ⟨fun _ ↦ inferInstance, fun _ ↦ .of_locallyPathConnectedSpace⟩ + +theorem pathComponent_eq_connectedComponent (x : X) : pathComponent x = connectedComponent x := + (pathComponent_subset_component x).antisymm <| + (IsClopen.pathComponent x).connectedComponent_subset (mem_pathComponent_self _) + +theorem connectedComponent_eq_iff_joined (x y : X) : + connectedComponent x = connectedComponent y ↔ Joined x y := by + rw [← mem_pathComponent_iff, pathComponent_eq_connectedComponent, eq_comm] + exact connectedComponent_eq_iff_mem + +theorem connectedComponentSetoid_eq_pathSetoid : connectedComponentSetoid X = pathSetoid X := + Setoid.ext connectedComponent_eq_iff_joined + +/-- In a locally path-connected space, connected components and path-connected components align -/ +def connectedComponentsEquivZerothHomotopy : ConnectedComponents X ≃ ZerothHomotopy X where + toFun := Quotient.map id (connectedComponent_eq_iff_joined · · |>.mp ·) + invFun := ZerothHomotopy.toConnectedComponents + left_inv := Quot.ind <| congrFun rfl + right_inv := Quot.ind <| congrFun rfl + +@[simp] +lemma connectedComponentsEquivZerothHomotopy_apply (x : X) : + connectedComponentsEquivZerothHomotopy ⟦x⟧ = (.mk x) := + rfl + +@[simp] +lemma coe_connectedComponentsEquivZerothHomotopy_symm : + ⇑connectedComponentsEquivZerothHomotopy.symm = ZerothHomotopy.toConnectedComponents (X := X) := + rfl + +lemma connectedComponentsEquivZerothHomotopy_symm_apply (x : X) : + connectedComponentsEquivZerothHomotopy.symm (.mk x) = ⟦x⟧ := + rfl + +theorem pathConnected_subset_basis {U : Set X} (h : IsOpen U) (hx : x ∈ U) : + (𝓝 x).HasBasis (fun s : Set X => s ∈ 𝓝 x ∧ IsPathConnected s ∧ s ⊆ U) id := + (path_connected_basis x).hasBasis_self_subset (IsOpen.mem_nhds h hx) + +theorem isOpen_isPathConnected_basis (x : X) : + (𝓝 x).HasBasis (fun s : Set X ↦ IsOpen s ∧ x ∈ s ∧ IsPathConnected s) id := by + refine ⟨fun s ↦ ⟨fun hs ↦ ?_, fun ⟨u, hu⟩ ↦ mem_nhds_iff.mpr ⟨u, hu.2, hu.1.1, hu.1.2.1⟩⟩⟩ + have ⟨u, hus, hu, hxu⟩ := mem_nhds_iff.mp hs + exact ⟨pathComponentIn u x, ⟨hu.pathComponentIn _, ⟨mem_pathComponentIn_self hxu, + isPathConnected_pathComponentIn hxu⟩⟩, pathComponentIn_subset.trans hus⟩ + +theorem Topology.IsOpenEmbedding.locallyPathConnectedSpace {e : Y → X} (he : IsOpenEmbedding e) : + LocallyPathConnectedSpace Y := + have (y : Y) : + (𝓝 y).HasBasis (fun s ↦ s ∈ 𝓝 (e y) ∧ IsPathConnected s ∧ s ⊆ range e) (e ⁻¹' ·) := + he.basis_nhds <| pathConnected_subset_basis he.isOpen_range (mem_range_self _) + .of_bases this fun x s ⟨_, hs, hse⟩ ↦ by + rwa [he.isPathConnected_iff, image_preimage_eq_of_subset hse] + +@[deprecated (since := "2026-06-21")] +alias Topology.IsOpenEmbedding.locPathConnectedSpace := + Topology.IsOpenEmbedding.locallyPathConnectedSpace + +theorem IsOpen.locallyPathConnectedSpace {U : Set X} (h : IsOpen U) : LocallyPathConnectedSpace U := + h.isOpenEmbedding_subtypeVal.locallyPathConnectedSpace + +@[deprecated (since := "2026-06-21")] +alias IsOpen.locPathConnectedSpace := IsOpen.locallyPathConnectedSpace + +theorem IsOpen.isConnected_iff_isPathConnected {U : Set X} (U_op : IsOpen U) : + IsConnected U ↔ IsPathConnected U := by + rw [isConnected_iff_connectedSpace, isPathConnected_iff_pathConnectedSpace] + haveI := U_op.locallyPathConnectedSpace + exact pathConnectedSpace_iff_connectedSpace.symm + +/-- Locally path-connected spaces are locally connected. -/ +instance : LocallyConnectedSpace X := by + refine ⟨forall_imp (fun x h ↦ ⟨fun s ↦ ?_⟩) isOpen_isPathConnected_basis⟩ + refine ⟨fun hs ↦ ?_, fun ⟨u, ⟨hu, hxu, _⟩, hus⟩ ↦ mem_nhds_iff.mpr ⟨u, hus, hu, hxu⟩⟩ + let ⟨u, ⟨hu, hxu, hu'⟩, hus⟩ := (h.mem_iff' s).mp hs + exact ⟨u, ⟨hu, hxu, hu'.isConnected⟩, hus⟩ + +/-- A space is locally path-connected iff all path components of open subsets are open. -/ +lemma locallyPathConnectedSpace_iff_isOpen_pathComponentIn {X : Type*} [TopologicalSpace X] : + LocallyPathConnectedSpace X ↔ ∀ (x : X) (u : Set X), IsOpen u → IsOpen (pathComponentIn u x) := + ⟨fun _ _ _ hu ↦ hu.pathComponentIn _, fun h ↦ ⟨fun x ↦ ⟨fun s ↦ by + refine ⟨fun hs ↦ ?_, fun ⟨_, ht⟩ ↦ Filter.mem_of_superset ht.1.1 ht.2⟩ + let ⟨u, hu⟩ := mem_nhds_iff.mp hs + exact ⟨pathComponentIn u x, ⟨(h x u hu.2.1).mem_nhds (mem_pathComponentIn_self hu.2.2), + isPathConnected_pathComponentIn hu.2.2⟩, pathComponentIn_subset.trans hu.1⟩⟩⟩⟩ + +@[deprecated (since := "2026-06-21")] +alias locPathConnectedSpace_iff_isOpen_pathComponentIn := + locallyPathConnectedSpace_iff_isOpen_pathComponentIn + +/-- A space is locally path-connected iff all path components of open subsets are neighbourhoods. -/ +lemma locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds {X : Type*} [TopologicalSpace X] : + LocallyPathConnectedSpace X ↔ + ∀ x : X, ∀ u : Set X, IsOpen u → x ∈ u → pathComponentIn u x ∈ nhds x := by + rw [locallyPathConnectedSpace_iff_isOpen_pathComponentIn] + simp_rw [forall_comm (β := Set X), ← imp_forall_iff] + refine forall_congr' fun u ↦ imp_congr_right fun _ ↦ ?_ + exact ⟨fun h x hxu ↦ (h x).mem_nhds (mem_pathComponentIn_self hxu), + fun h x ↦ isOpen_iff_mem_nhds.mpr fun y hy ↦ + pathComponentIn_congr hy ▸ h y <| pathComponentIn_subset hy⟩ + +@[deprecated (since := "2026-06-21")] +alias locPathConnectedSpace_iff_pathComponentIn_mem_nhds := + locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds + +/-- Any topology coinduced by a locally path-connected topology is locally path-connected. -/ +lemma LocallyPathConnectedSpace.coinduced {Y : Type*} (f : X → Y) : + @LocallyPathConnectedSpace Y (.coinduced f ‹_›) := by + let _ := TopologicalSpace.coinduced f ‹_›; have hf : Continuous f := continuous_coinduced_rng + refine locallyPathConnectedSpace_iff_isOpen_pathComponentIn.mpr fun y u hu ↦ + isOpen_coinduced.mpr <| isOpen_iff_mem_nhds.mpr fun x hx ↦ ?_ + have hx' := preimage_mono pathComponentIn_subset hx + refine mem_nhds_iff.mpr ⟨pathComponentIn (f ⁻¹' u) x, ?_, + (hu.preimage hf).pathComponentIn _, mem_pathComponentIn_self hx'⟩ + rw [← image_subset_iff, ← pathComponentIn_congr hx] + exact ((isPathConnected_pathComponentIn hx').image hf).subset_pathComponentIn + ⟨x, mem_pathComponentIn_self hx', rfl⟩ <| + (image_mono pathComponentIn_subset).trans <| u.image_preimage_subset f + +@[deprecated (since := "2026-06-21")] +alias LocPathConnectedSpace.coinduced := LocallyPathConnectedSpace.coinduced + +/-- Quotients of locally path-connected spaces are locally path-connected. -/ +lemma Topology.IsQuotientMap.locallyPathConnectedSpace {f : X → Y} (h : IsQuotientMap f) : + LocallyPathConnectedSpace Y := + h.isCoinducing.eq_coinduced ▸ LocallyPathConnectedSpace.coinduced f + +@[deprecated (since := "2026-06-21")] +alias Topology.IsQuotientMap.locPathConnectedSpace := + Topology.IsQuotientMap.locallyPathConnectedSpace + +/-- Quotients of locally path-connected spaces are locally path-connected. -/ +instance Quot.locallyPathConnectedSpace {r : X → X → Prop} : LocallyPathConnectedSpace (Quot r) := + isQuotientMap_quot_mk.locallyPathConnectedSpace + +@[deprecated (since := "2026-06-21")] +alias Quot.locPathConnectedSpace := Quot.locallyPathConnectedSpace + +/-- Quotients of locally path-connected spaces are locally path-connected. -/ +instance Quotient.locallyPathConnectedSpace {s : Setoid X} : + LocallyPathConnectedSpace (Quotient s) := + isQuotientMap_quotient_mk'.locallyPathConnectedSpace + +@[deprecated (since := "2026-06-21")] +alias Quotient.locPathConnectedSpace := Quotient.locallyPathConnectedSpace + +/-- Disjoint unions of locally path-connected spaces are locally path-connected. -/ +instance Sum.locallyPathConnectedSpace [LocallyPathConnectedSpace Y] : + LocallyPathConnectedSpace (X ⊕ Y) := by + rw [locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff] + obtain x | y := x + · refine ⟨Sum.inl '' pathComponentIn (Sum.inl ⁻¹' u) x, ?_, ?_, ?_⟩ + · apply IsPathConnected.subset_pathComponentIn + · exact (isPathConnected_pathComponentIn (by exact hxu)).image continuous_inl + · exact ⟨x, mem_pathComponentIn_self hxu, rfl⟩ + · exact (image_mono pathComponentIn_subset).trans (u.image_preimage_subset _) + · exact isOpenMap_inl _ <| (hu.preimage continuous_inl).pathComponentIn _ + · exact ⟨x, mem_pathComponentIn_self hxu, rfl⟩ + · refine ⟨Sum.inr '' pathComponentIn (Sum.inr ⁻¹' u) y, ?_, ?_, ?_⟩ + · apply IsPathConnected.subset_pathComponentIn + · exact (isPathConnected_pathComponentIn (by exact hxu)).image continuous_inr + · exact ⟨y, mem_pathComponentIn_self hxu, rfl⟩ + · exact (image_mono pathComponentIn_subset).trans (u.image_preimage_subset _) + · exact isOpenMap_inr _ <| (hu.preimage continuous_inr).pathComponentIn _ + · exact ⟨y, mem_pathComponentIn_self hxu, rfl⟩ + +@[deprecated (since := "2026-06-21")] +alias Sum.locPathConnectedSpace := Sum.locallyPathConnectedSpace + +/-- Disjoint unions of locally path-connected spaces are locally path-connected. -/ +instance Sigma.locallyPathConnectedSpace {X : ι → Type*} + [(i : ι) → TopologicalSpace (X i)] [(i : ι) → LocallyPathConnectedSpace (X i)] : + LocallyPathConnectedSpace ((i : ι) × X i) := by + rw [locallyPathConnectedSpace_iff_pathComponentIn_mem_nhds]; intro x u hu hxu; rw [mem_nhds_iff] + refine ⟨(Sigma.mk x.1) '' pathComponentIn ((Sigma.mk x.1) ⁻¹' u) x.2, ?_, ?_, ?_⟩ + · apply IsPathConnected.subset_pathComponentIn + · exact (isPathConnected_pathComponentIn (by exact hxu)).image continuous_sigmaMk + · exact ⟨x.2, mem_pathComponentIn_self hxu, rfl⟩ + · exact (image_mono pathComponentIn_subset).trans (u.image_preimage_subset _) + · exact isOpenMap_sigmaMk _ <| (hu.preimage continuous_sigmaMk).pathComponentIn _ + · exact ⟨x.2, mem_pathComponentIn_self hxu, rfl⟩ + +@[deprecated (since := "2026-06-21")] +alias Sigma.locPathConnectedSpace := Sigma.locallyPathConnectedSpace + +instance AlexandrovDiscrete.locallyPathConnectedSpace [AlexandrovDiscrete X] : + LocallyPathConnectedSpace X := by + apply LocallyPathConnectedSpace.of_bases nhds_basis_nhdsKer_singleton + simp only [forall_const, IsPathConnected, mem_nhdsKer_singleton] + intro x + exists x, specializes_rfl + intro y hy + symm + apply hy.joinedIn <;> rewrite [mem_nhdsKer_singleton] <;> [assumption; rfl] + +@[deprecated (since := "2026-06-21")] +alias AlexandrovDiscrete.locPathConnectedSpace := AlexandrovDiscrete.locallyPathConnectedSpace + +/-- If a space is locally path-connected, the topology of its path components is discrete. -/ +instance : DiscreteTopology <| ZerothHomotopy X := by + refine discreteTopology_iff_isOpen_singleton.mpr fun c ↦ ?_ + obtain ⟨x, rfl⟩ := ZerothHomotopy.mk_surjective c + rw [← ZerothHomotopy.isQuotientMap_mk.isOpen_preimage] + grind [ZerothHomotopy.preimage_singleton_eq_pathComponent, IsOpen.pathComponent] + +/-- A locally path-connected compact space has finitely many path components. -/ +instance [CompactSpace X] : Finite <| ZerothHomotopy X := + finite_of_compact_of_discrete + +end LocallyPathConnectedSpace diff --git a/Mathlib/Topology/Connected/PathComponentOne.lean b/Mathlib/Topology/Connected/PathComponentOne.lean index c992c88d39b2aa..862803a9493bf8 100644 --- a/Mathlib/Topology/Connected/PathComponentOne.lean +++ b/Mathlib/Topology/Connected/PathComponentOne.lean @@ -6,7 +6,7 @@ Authors: Jireh Loreaux module public import Mathlib.Topology.Algebra.OpenSubgroup -public import Mathlib.Topology.Connected.LocPathConnected +public import Mathlib.Topology.Connected.LocallyPathConnected /-! # The path component of the identity in a locally path connected topological group @@ -27,7 +27,7 @@ as an open normal subgroup. It is, in fact, clopen. -/ /-- 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] : + [IsTopologicalGroup G] [LocallyPathConnectedSpace G] : OpenNormalSubgroup G where toSubgroup := .pathComponentOne G isOpen' := .pathComponent 1 @@ -36,7 +36,7 @@ def OpenNormalSubgroup.pathComponentOne [Group G] namespace OpenNormalSubgroup @[to_additive] -instance [Group G] [IsTopologicalGroup G] [LocPathConnectedSpace G] : +instance [Group G] [IsTopologicalGroup G] [LocallyPathConnectedSpace G] : IsClosed (OpenNormalSubgroup.pathComponentOne G : Set G) := .pathComponent 1 diff --git a/Mathlib/Topology/Constructions/SumProd.lean b/Mathlib/Topology/Constructions/SumProd.lean index 8c417843dbf7e1..4bfea412958fbd 100644 --- a/Mathlib/Topology/Constructions/SumProd.lean +++ b/Mathlib/Topology/Constructions/SumProd.lean @@ -6,7 +6,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Patrick Massot module public import Mathlib.Topology.Homeomorph.Defs -public import Mathlib.Topology.Maps.Basic +public import Mathlib.Topology.Maps.OpenQuotient public import Mathlib.Topology.Separation.SeparatedNhds /-! @@ -358,6 +358,16 @@ theorem ContinuousAt.prodMap' {f : X → Z} {g : Y → W} {x : X} {y : Y} (hf : (hg : ContinuousAt g y) : ContinuousAt (Prod.map f g) (x, y) := hf.prodMap hg +@[simp] +theorem continuousAt_prodMap_iff {f : X → Z} {g : Y → W} {x : X} {y : Y} : + ContinuousAt (Prod.map f g) (x, y) ↔ ContinuousAt f x ∧ ContinuousAt g y := by + simp [ContinuousAt, nhds_prod_eq, tendsto_iff_comap, comap_prodMap_prod] + +@[simp] +theorem continuous_prodMap_iff [Nonempty Z] [Nonempty W] {f : Z → X} {g : W → Y} : + Continuous (Prod.map f g) ↔ Continuous f ∧ Continuous g := by + simp [continuous_iff_continuousAt, forall_and] + theorem ContinuousAt.comp₂ {f : Y × Z → W} {g : X → Y} {h : X → Z} {x : X} (hf : ContinuousAt f (g x, h x)) (hg : ContinuousAt g x) (hh : ContinuousAt h x) : ContinuousAt (fun x ↦ f (g x, h x)) x := @@ -494,11 +504,17 @@ theorem isOpen_prod_iff' {s : Set X} {t : Set Y} : simp only [st.1.ne_empty, st.2.ne_empty, or_false] at H exact H.1.prod H.2 +theorem isOpenQuotientMap_fst [Nonempty Y] : IsOpenQuotientMap (Prod.fst : X × Y → X) := + ⟨Prod.fst_surjective, continuous_fst, isOpenMap_fst⟩ + +theorem isOpenQuotientMap_snd [Nonempty X] : IsOpenQuotientMap (Prod.snd : X × Y → Y) := + ⟨Prod.snd_surjective, continuous_snd, isOpenMap_snd⟩ + theorem isQuotientMap_fst [Nonempty Y] : IsQuotientMap (Prod.fst : X × Y → X) := - isOpenMap_fst.isQuotientMap continuous_fst Prod.fst_surjective + isOpenQuotientMap_fst.isQuotientMap theorem isQuotientMap_snd [Nonempty X] : IsQuotientMap (Prod.snd : X × Y → Y) := - isOpenMap_snd.isQuotientMap continuous_snd Prod.snd_surjective + isOpenQuotientMap_snd.isQuotientMap theorem closure_prod_eq {s : Set X} {t : Set Y} : closure (s ×ˢ t) = closure s ×ˢ closure t := ext fun ⟨a, b⟩ => by @@ -589,6 +605,15 @@ protected theorem IsOpenMap.prodMap {f : X → Y} {g : Z → W} (hf : IsOpenMap rw [nhds_prod_eq, nhds_prod_eq, ← Filter.prod_map_map_eq'] exact Filter.prod_mono (hf.nhds_le a) (hg.nhds_le b) +@[simp] +theorem isOpenMap_prodMap_iff [Nonempty X] [Nonempty Z] {f : X → Y} {g : Z → W} : + IsOpenMap (Prod.map f g) ↔ IsOpenMap f ∧ IsOpenMap g := by + refine ⟨fun h ↦ ⟨?_, ?_⟩, fun ⟨hf, hg⟩ ↦ hf.prodMap hg⟩ + · rw [(isOpenQuotientMap_fst (Y := Z)).isOpenMap_iff] + exact isOpenMap_fst.comp h + · rw [(isOpenQuotientMap_snd (X := X)).isOpenMap_iff] + exact isOpenMap_snd.comp h + protected lemma Topology.IsOpenEmbedding.prodMap {f : X → Y} {g : Z → W} (hf : IsOpenEmbedding f) (hg : IsOpenEmbedding g) : IsOpenEmbedding (Prod.map f g) := .of_isEmbedding_isOpenMap (hf.1.prodMap hg.1) (hf.isOpenMap.prodMap hg.isOpenMap) @@ -612,6 +637,13 @@ theorem IsOpenQuotientMap.prodMap {f : X → Y} {g : Z → W} (hf : IsOpenQuotie (hg : IsOpenQuotientMap g) : IsOpenQuotientMap (Prod.map f g) := ⟨.prodMap hf.1 hg.1, .prodMap hf.2 hg.2, .prodMap hf.3 hg.3⟩ +@[simp] +theorem isOpenQuotientMap_prodMap_iff [Nonempty X] [Nonempty Z] {f : X → Y} {g : Z → W} : + IsOpenQuotientMap (Prod.map f g) ↔ IsOpenQuotientMap f ∧ IsOpenQuotientMap g := by + have : Nonempty Y := .map f inferInstance + have : Nonempty W := .map g inferInstance + grind [isOpenQuotientMap_iff, continuous_prodMap_iff, isOpenMap_prodMap_iff, Prod.map_surjective] + theorem TopologicalSpace.prod_mono {α β : Type*} {σ₁ σ₂ : TopologicalSpace α} {τ₁ τ₂ : TopologicalSpace β} (hσ : σ₁ ≤ σ₂) (hτ : τ₁ ≤ τ₂) : @instTopologicalSpaceProd α β σ₁ τ₁ ≤ @instTopologicalSpaceProd α β σ₂ τ₂ := @@ -800,7 +832,7 @@ theorem IsOpenMap.sumElim {f : X → Z} {g : Y → Z} (hf : IsOpenMap f) (hg : I IsOpenMap (Sum.elim f g) := isOpenMap_sumElim.2 ⟨hf, hg⟩ -lemma IsOpenEmbedding.sumElim {f : X → Z} {g : Y → Z} +lemma Topology.IsOpenEmbedding.sumElim {f : X → Z} {g : Y → Z} (hf : IsOpenEmbedding f) (hg : IsOpenEmbedding g) (h : Injective (Sum.elim f g)) : IsOpenEmbedding (Sum.elim f g) := by rw [isOpenEmbedding_iff_continuous_injective_isOpenMap] at hf hg ⊢ @@ -830,7 +862,7 @@ theorem IsClosedMap.sumElim {f : X → Z} {g : Y → Z} (hf : IsClosedMap f) (hg IsClosedMap (Sum.elim f g) := isClosedMap_sumElim.2 ⟨hf, hg⟩ -lemma IsClosedEmbedding.sumElim {f : X → Z} {g : Y → Z} +lemma Topology.IsClosedEmbedding.sumElim {f : X → Z} {g : Y → Z} (hf : IsClosedEmbedding f) (hg : IsClosedEmbedding g) (h : Injective (Sum.elim f g)) : IsClosedEmbedding (Sum.elim f g) := by rw [IsClosedEmbedding.isClosedEmbedding_iff_continuous_injective_isClosedMap] at hf hg ⊢ @@ -974,16 +1006,17 @@ theorem Topology.IsInducing.disjoint_of_sumElim_aux (h : IsInducing (Sum.elim f exact disjoint_image_inl_image_inr exact B.mono_left A -theorem IsOpenEmbedding.sumSwap : IsOpenEmbedding (@Sum.swap X Y) := +theorem Topology.IsOpenEmbedding.sumSwap : IsOpenEmbedding (@Sum.swap X Y) := (Homeomorph.sumComm X Y).isOpenEmbedding -theorem IsInducing.sumSwap : IsInducing (@Sum.swap X Y) := IsOpenEmbedding.sumSwap.isInducing +theorem Topology.IsInducing.sumSwap : IsInducing (@Sum.swap X Y) := + IsOpenEmbedding.sumSwap.isInducing theorem isInducing_sumElim : IsInducing (Sum.elim f g) ↔ IsInducing f ∧ IsInducing g ∧ Disjoint (closure (range f)) (range g) ∧ Disjoint (range f) (closure (range g)) := ⟨fun h ↦ ⟨h.sumElim_left, h.sumElim_right, h.disjoint_of_sumElim_aux, - ((Sum.elim_swap ▸ h.comp IsInducing.sumSwap).disjoint_of_sumElim_aux ).symm⟩, + ((Sum.elim_swap ▸ h.comp .sumSwap).disjoint_of_sumElim_aux ).symm⟩, fun ⟨hf, hg, hFg, hfG⟩ ↦ hf.sumElim hg hFg hfG⟩ lemma Topology.IsInducing.sumElim_of_separatedNhds diff --git a/Mathlib/Topology/ContinuousMap/Algebra.lean b/Mathlib/Topology/ContinuousMap/Algebra.lean index 5d39eabc7e3970..926994ae9b3157 100644 --- a/Mathlib/Topology/ContinuousMap/Algebra.lean +++ b/Mathlib/Topology/ContinuousMap/Algebra.lean @@ -492,8 +492,6 @@ end ContinuousMap end RingStructure -attribute [local ext] Subtype.eq - section ModuleStructure /-! diff --git a/Mathlib/Topology/ContinuousMap/Compact.lean b/Mathlib/Topology/ContinuousMap/Compact.lean index 221cbe425e19d7..83583586041b6c 100644 --- a/Mathlib/Topology/ContinuousMap/Compact.lean +++ b/Mathlib/Topology/ContinuousMap/Compact.lean @@ -243,6 +243,13 @@ theorem norm_restrict_mono_set {X : Type*} [TopologicalSpace X] (f : C(X, E)) {K L : TopologicalSpace.Compacts X} (hKL : K ≤ L) : ‖f.restrict K‖ ≤ ‖f.restrict L‖ := (norm_le _ (norm_nonneg _)).mpr fun x => norm_coe_le_norm (f.restrict L) <| Set.inclusion hKL x +lemma norm_eq_norm_coeFn [Fintype α] : ‖f‖ = ‖(f : α → E)‖ := by + apply le_antisymm + · rw [ContinuousMap.norm_le _ (by positivity)] + exact norm_le_pi_norm _ + · rw [pi_norm_le_iff_of_nonneg (by positivity)] + exact f.norm_coe_le_norm + end section diff --git a/Mathlib/Topology/ContinuousMap/Ideals.lean b/Mathlib/Topology/ContinuousMap/Ideals.lean index b13c306a8ecc46..7ab99056669275 100644 --- a/Mathlib/Topology/ContinuousMap/Ideals.lean +++ b/Mathlib/Topology/ContinuousMap/Ideals.lean @@ -264,7 +264,7 @@ theorem idealOfSet_ofIdeal_eq_closure (I : Ideal C(X, 𝕜)) : refine ⟨{y : X | g y ≠ 0} ∩ t, mem_nhdsWithin_iff_exists_mem_nhds_inter.mpr ⟨_, this, Set.Subset.rfl⟩, - ⟨⟨fun x => ‖g x‖₊ ^ 2, (map_continuous g).nnnorm.pow 2⟩, ?_, fun x hx => + ⟨⟨fun x => ‖g x‖₊ ^ 2, (map_continuous g).nnnorm.fun_pow 2⟩, ?_, fun x hx => pow_pos (norm_pos_iff.mpr hx.1) 2⟩⟩ convert! I.mul_mem_left (star g) hI ext diff --git a/Mathlib/Topology/Covering.lean b/Mathlib/Topology/Covering.lean deleted file mode 100644 index f1bd5c1e8ed341..00000000000000 --- a/Mathlib/Topology/Covering.lean +++ /dev/null @@ -1,5 +0,0 @@ -module -- shake: keep-all - -public import Mathlib.Topology.Covering.Basic - -deprecated_module (since := "2025-12-10") diff --git a/Mathlib/Topology/Covering/Basic.lean b/Mathlib/Topology/Covering/Basic.lean index 45d1952f2dde26..ad257b2dbc7685 100644 --- a/Mathlib/Topology/Covering/Basic.lean +++ b/Mathlib/Topology/Covering/Basic.lean @@ -497,7 +497,7 @@ variable (f) in theorem IsDiscrete.of_openPartialHomeomorph {t : Set E} {x : X} (htx : t ⊆ f ⁻¹' {x}) (hf : ∀ e ∈ t, ∃ φ : OpenPartialHomeomorph E X, e ∈ φ.source ∧ φ = f) : IsDiscrete t := - isDiscrete_iff_forall_exists_isOpen.mpr fun e he ↦ by + isDiscrete_iff_forall_mem_exists_isOpen.mpr fun e he ↦ by obtain ⟨φ, hφ, rfl⟩ := hf e he exact ⟨_, φ.open_source, subset_antisymm (fun e' he' ↦ φ.injOn he'.1 hφ <| (htx he'.2).trans (htx he).symm) <| Set.singleton_subset_iff.mpr ⟨hφ, he⟩⟩ @@ -555,11 +555,18 @@ theorem IsClosedMap.isEvenlyCovered_of_openPartialHomeomorph [T2Space E] {x : X} /-- If `f : E → X` is a closed map between topological spaces with `E` Hausdorff, and `s` is a subset of `X` on which `f` has finite fibers, such that `f` restricts to a homeomorphism on a neighborhood of every point of `f ⁻¹' s`, then `f` is a covering map on `s`. -/ -theorem IsClosedMap.isCoveringMapOn_of_openPartialHomeomorph [T2Space E] +theorem IsClosedMap.isCoveringMapOn_of_isLocalHomeomorphOn [T2Space E] (hf : IsClosedMap f) (hs : ∀ x ∈ s, (f ⁻¹' {x}).Finite) - (h : ∀ e ∈ f ⁻¹' s, ∃ φ : OpenPartialHomeomorph E X, e ∈ φ.source ∧ φ = f) : - IsCoveringMapOn f s := - fun x hx ↦ hf.isEvenlyCovered_of_openPartialHomeomorph (hs x hx) fun e he ↦ h e (by apply he ▸ hx) + (h : IsLocalHomeomorphOn f (f ⁻¹' s)) : + IsCoveringMapOn f s := by + intro x hx + refine hf.isEvenlyCovered_of_openPartialHomeomorph (hs x hx) fun e he ↦ ?_ + obtain ⟨φ, hφ, rfl⟩ := h e (by aesop) + aesop + +@[deprecated (since := "2026-06-25")] +alias IsClosedMap.isCoveringMapOn_of_openPartialHomeomorph := + IsClosedMap.isCoveringMapOn_of_isLocalHomeomorphOn /-- If `f : E → X` is a continuous map between Hausdorff spaces with `E` compact, and `f` restricts to a homeomorphism on a neighborhood of every point of a fiber `f ⁻¹' {x}`, @@ -578,8 +585,27 @@ then `f` is a covering map on `s`. For example, `s` can be taken to be the set of regular values of a C¹ map `f : E → X` where `E` and `X` are manifolds of the same dimension with `E` compact, according to the inverse function theorem (see `ContDiffAt.toOpenPartialHomeomorph`). -/ -theorem IsCoveringMapOn.of_openPartialHomeomorph +theorem IsCoveringMapOn.of_isLocalHomeomorphOn [T2Space E] [T2Space X] [CompactSpace E] (hf : Continuous f) - (h : ∀ e ∈ f ⁻¹' s, ∃ φ : OpenPartialHomeomorph E X, e ∈ φ.source ∧ φ = f) : - IsCoveringMapOn f s := - fun x hx ↦ .of_openPartialHomeomorph hf fun e he ↦ h e (by apply he ▸ hx) + (h : IsLocalHomeomorphOn f (f ⁻¹' s)) : + IsCoveringMapOn f s := by + intro x hx + refine .of_openPartialHomeomorph hf fun e he ↦ ?_ + obtain ⟨φ, hφ, rfl⟩ := h e (by aesop) + aesop + +@[deprecated (since := "2026-06-25")] +alias IsCoveringMapOn.of_openPartialHomeomorph := IsCoveringMapOn.of_isLocalHomeomorphOn + +@[simp] +lemma isLocalHomeomorph_iff_isCoveringMap [T2Space E] [T2Space X] [CompactSpace E] : + IsLocalHomeomorph f ↔ IsCoveringMap f := by + refine ⟨fun h ↦ ?_, IsCoveringMap.isLocalHomeomorph⟩ + have hf : Continuous f := by + rw [continuous_iff_continuousAt] + intro e + obtain ⟨φ, hφ, rfl⟩ := h e + exact φ.continuousAt hφ + rw [isCoveringMap_iff_isCoveringMapOn_univ] + apply IsCoveringMapOn.of_isLocalHomeomorphOn hf + simpa [← isLocalHomeomorph_iff_isLocalHomeomorphOn_univ] diff --git a/Mathlib/Topology/Covering/Quotient.lean b/Mathlib/Topology/Covering/Quotient.lean index 931aaa815baca6..476030c955bc6e 100644 --- a/Mathlib/Topology/Covering/Quotient.lean +++ b/Mathlib/Topology/Covering/Quotient.lean @@ -41,6 +41,26 @@ structure IsQuotientCoveringMap : Prop extends IsQuotientMap f, ContinuousConstS attribute [to_additive] isQuotientCoveringMap_iff +lemma IsAddQuotientCoveringMap.toMultiplicative (G) [AddGroup G] [AddAction G E] + (hf : IsAddQuotientCoveringMap f G) : + IsQuotientCoveringMap f (Multiplicative G) where + __ := hf.toIsQuotientMap + continuous_const_smul g := by simpa using hf.continuous_const_vadd (Multiplicative.ofAdd.symm g) + apply_eq_iff_mem_orbit {e₁ e₂} := by simp [hf.apply_eq_iff_mem_orbit] + disjoint e := by + obtain ⟨U, hU, hU'⟩ := hf.disjoint e + exact ⟨U, hU, fun g ↦ by simpa using hU' (Multiplicative.ofAdd.symm g)⟩ + +lemma IsQuotientCoveringMap.toAdditive (G) [Group G] [MulAction G E] + (hf : IsQuotientCoveringMap f G) : + IsAddQuotientCoveringMap f (Additive G) where + __ := hf.toIsQuotientMap + continuous_const_vadd g := by simpa using hf.continuous_const_smul (Additive.ofMul.symm g) + apply_eq_iff_mem_orbit {e₁ e₂} := by simp [hf.apply_eq_iff_mem_orbit] + disjoint e := by + obtain ⟨U, hU, hU'⟩ := hf.disjoint e + exact ⟨U, hU, fun g ↦ by simpa using hU' (Additive.ofMul.symm g)⟩ + namespace IsQuotientCoveringMap @[to_additive] theorem subgroup_congr (S S' : Subgroup G) (eq : S = S') : diff --git a/Mathlib/Topology/DiscreteSubset.lean b/Mathlib/Topology/DiscreteSubset.lean index 108b7ca92a5d8d..b949ac0ba8702a 100644 --- a/Mathlib/Topology/DiscreteSubset.lean +++ b/Mathlib/Topology/DiscreteSubset.lean @@ -65,10 +65,39 @@ lemma discreteTopology_subtype_iff' {S : Set Y} : simp [discreteTopology_iff_isOpen_singleton, isOpen_induced_iff, Set.ext_iff] grind -theorem isDiscrete_iff_forall_exists_isOpen {S : Set Y} : - IsDiscrete S ↔ ∀ y ∈ S, ∃ U, IsOpen U ∧ U ∩ S = {y} := by +/-- A set `s` is discrete iff for every `y ∈ s` there is an open `u` with `u ∩ s = {y}`. +See `isDiscrete_iff_forall_subset_exists_isOpen'` for a related version of this with subsets. -/ +theorem isDiscrete_iff_forall_mem_exists_isOpen {s : Set Y} : + IsDiscrete s ↔ ∀ y ∈ s, ∃ u, IsOpen u ∧ u ∩ s = {y} := by rw [isDiscrete_iff_discreteTopology, discreteTopology_subtype_iff'] +@[deprecated (since := "2026-06-24")] +alias isDiscrete_iff_forall_exists_isOpen := isDiscrete_iff_forall_mem_exists_isOpen + +/-- A set `s` is discrete iff for every `t ⊆ s` there is an open `u` with `u ∩ s = t`. +See `isDiscrete_iff_forall_mem_exists_isOpen` for a similar version of this with singletons. -/ +theorem isDiscrete_iff_forall_subset_exists_isOpen {s : Set X} : + IsDiscrete s ↔ ∀ t ⊆ s, ∃ u, IsOpen u ∧ u ∩ s = t := by + simp_rw [isDiscrete_iff_discreteTopology, discreteTopology_iff_forall_isOpen, + isOpen_induced_iff, ← image_eq_image (Subtype.val_injective), Subtype.image_preimage_coe, + Subtype.forall_set_subtype (p := fun t ↦ ∃ u, IsOpen u ∧ s ∩ u = t), inter_comm] + +/-- A set `s` is discrete iff for every `t ⊆ s` there is a closed `u` with `u ∩ s = t`. -/ +theorem isDiscrete_iff_forall_mem_exists_isClosed {S : Set X} : + IsDiscrete S ↔ ∀ s ⊆ S, ∃ U, IsClosed U ∧ U ∩ S = s := by + rw [isDiscrete_iff_forall_subset_exists_isOpen] + constructor <;> intro h s sS + · obtain ⟨U, Uo, Us⟩ := h (sᶜ ∩ S) inter_subset_right + exact ⟨Uᶜ, isClosed_compl_iff.mpr Uo, by rw [left_eq_inter.mpr sS]; simp_all [Set.ext_iff]⟩ + · obtain ⟨U, Uo, Us⟩ := h (sᶜ ∩ S) inter_subset_right + exact ⟨Uᶜ, isOpen_compl_iff.mpr Uo, by rw [left_eq_inter.mpr sS]; simp_all [Set.ext_iff]⟩ + +theorem isClosed_of_subset_discrete_closed {s t : Set X} (sd : s ⊆ t) + (ht : IsDiscrete t) (tc : IsClosed t) : IsClosed s := by + obtain ⟨_, rp, rt⟩ := isDiscrete_iff_forall_mem_exists_isClosed.mp ht s sd + rw [← rt] + exact rp.inter tc + lemma Set.Subsingleton.isDiscrete (hs : s.Subsingleton) : IsDiscrete s := have : Subsingleton s := (Set.subsingleton_coe s).mpr hs ⟨inferInstance⟩ @@ -112,7 +141,7 @@ lemma IsOpenMap.isDiscrete_range [DiscreteTopology X] (hf : IsOpenMap f) : lemma IsDiscrete.image (hs : IsDiscrete s) (hf : IsInducing f) : IsDiscrete (f '' s) := by simp_all [isDiscrete_iff_nhdsWithin, ← hf.map_nhdsWithin_eq s] -lemma IsInducing.isDiscrete_range [DiscreteTopology X] (hf : IsInducing f) : +lemma Topology.IsInducing.isDiscrete_range [DiscreteTopology X] (hf : IsInducing f) : IsDiscrete (Set.range f) := by simpa using IsDiscrete.univ.image hf diff --git a/Mathlib/Topology/EMetricSpace/BoundedVariation.lean b/Mathlib/Topology/EMetricSpace/BoundedVariation.lean index 6d75d4a004f477..ad2563a2b56bfb 100644 --- a/Mathlib/Topology/EMetricSpace/BoundedVariation.lean +++ b/Mathlib/Topology/EMetricSpace/BoundedVariation.lean @@ -159,6 +159,19 @@ theorem mono (f : α → E) {s t : Set α} (hst : t ⊆ s) : eVariationOn f t rintro ⟨n, ⟨u, hu, ut⟩⟩ exact sum_le hu fun i => hst (ut i) +theorem eq_biSup_inter_Icc {f : α → E} {s : Set α} : eVariationOn f s = + ⨆ p ∈ {p : α × α | p.1 ∈ s ∧ p.2 ∈ s ∧ p.1 ≤ p.2}, eVariationOn f (s ∩ Icc p.1 p.2) := by + apply le_antisymm ?_ (by simp [iSup_le_iff, mono f inter_subset_left]) + rw [eVariationOn] + simp only [iSup_le_iff, Prod.forall, Subtype.forall, and_imp] + intro n u hu hus + calc ∑ x ∈ Finset.range n, edist (f (u (x + 1))) (f (u x)) + _ ≤ eVariationOn f (s ∩ Icc (u 0) (u n)) := + sum_le_of_monotoneOn_Iic (hu.monotoneOn _) (by grind [Monotone]) + _ ≤ ⨆ p ∈ {p : α × α | p.1 ∈ s ∧ p.2 ∈ s ∧ p.1 ≤ p.2}, eVariationOn f (s ∩ Icc p.1 p.2) := by + apply le_biSup (f := fun (p : α × α) ↦ eVariationOn f (s ∩ Icc p.1 p.2)) (i := (u 0, u n)) + grind [Monotone] + theorem _root_.BoundedVariationOn.mono {f : α → E} {s : Set α} (h : BoundedVariationOn f s) {t : Set α} (ht : t ⊆ s) : BoundedVariationOn f t := ne_top_of_le_ne_top h (eVariationOn.mono f ht) @@ -493,7 +506,8 @@ open OrderDual convert! comp_eq_of_antitoneOn f ofDual fun _ _ _ _ => id simp only [Equiv.image_preimage] -lemma _root_.BoundedVariationOn.ofDual {f : α → E} {s : Set α} (hf : BoundedVariationOn f s) : +protected lemma _root_.BoundedVariationOn.ofDual + {f : α → E} {s : Set α} (hf : BoundedVariationOn f s) : BoundedVariationOn (f ∘ ofDual) (ofDual ⁻¹' s) := by simpa [BoundedVariationOn] using hf @@ -501,51 +515,109 @@ lemma _root_.BoundedVariationOn.ofDual {f : α → E} {s : Set α} (hf : Bounded BoundedVariationOn (f ∘ ofDual) (ofDual ⁻¹' s) ↔ BoundedVariationOn f s := ⟨fun h ↦ h.ofDual, fun h ↦ h.ofDual⟩ +protected lemma _root_.LocallyBoundedVariationOn.ofDual {f : α → E} {s : Set α} + (hf : LocallyBoundedVariationOn f s) : + LocallyBoundedVariationOn (f ∘ ofDual) (ofDual ⁻¹' s) := by + intro x y hx hy + rw [← toDual_ofDual x, ← toDual_ofDual y, Icc_toDual, ← preimage_inter] + apply BoundedVariationOn.ofDual (hf (ofDual y) (ofDual x) hy hx) + +@[simp] lemma locallyBoundedVariation_ofDual {f : α → E} {s : Set α} : + LocallyBoundedVariationOn (f ∘ ofDual) (ofDual ⁻¹' s) ↔ LocallyBoundedVariationOn f s := + ⟨fun h ↦ h.ofDual, fun h ↦ h.ofDual⟩ + end Monotone /-! ### Left and right limits of bounded variation functions -/ +/-- The variation of a function on `Iic a` is the sum of the variation on `Iio a` and the +contribution of `a`, i.e., the distance between the left limit and the value at `a`. +We give a version relative to a set `s`. -/ +theorem eVariationOn_on_inter_Iic_eq_Iio_add_edist + [TopologicalSpace α] [OrderTopology α] {f : α → E} {s : Set α} {a : α} {l : E} + (h : (𝓝[s ∩ Iio a] a).NeBot) (ha : a ∈ s) + (h'f : Tendsto f (𝓝[s ∩ Iio a] a) (𝓝 l)) : + eVariationOn f (s ∩ Iic a) = eVariationOn f (s ∩ Iio a) + edist (f a) l := by + refine le_antisymm ?_ ?_ + · rw [eVariationOn_eq_strictMonoOn] + apply iSup_le + rintro ⟨n, u, u_mono, u_mem⟩ + have : u n ≤ a := (u_mem n (by simp)).2 + rcases this.eq_or_lt with hn | hn; swap + · exact (sum_le_of_monotoneOn_Iic u_mono.monotoneOn (by grind [StrictMonoOn])).trans le_self_add + cases n with + | zero => simp + | succ n => + have : Tendsto (fun y ↦ eVariationOn f (s ∩ Iio a) + edist (f a) (f y)) (𝓝[s ∩ Iio a] a) + (𝓝 (eVariationOn f (s ∩ Iio a) + edist (f a) l)) := + (Tendsto.edist tendsto_const_nhds h'f).const_add _ + apply ge_of_tendsto this + have : s ∩ Ioo (u n) a ∈ 𝓝[s ∩ Iio a] a := + inter_mem_nhdsWithin_inter self_mem_nhdsWithin (Ioo_mem_nhdsLT (by grind [StrictMonoOn])) + filter_upwards [this] with y hy + let v i := if i ≤ n then u i else if i = n + 1 then y else a + have A : ∑ i ∈ Finset.range (n + 1), edist (f (u (i + 1))) (f (u i)) + ≤ ∑ i ∈ Finset.range (n + 2), edist (f (v (i + 1))) (f (v i)) := by + simp only [Finset.sum_range_succ, add_assoc] + gcongr with i h + · grind + · grw [add_comm (edist _ _), ← edist_triangle] + grind + have B : ∑ i ∈ Finset.range (n + 2), edist (f (v (i + 1))) (f (v i)) ≤ + eVariationOn f (s ∩ Iio a) + edist (f a) (f y) := by + rw [Finset.sum_range_succ] + gcongr + · apply sum_le_of_monotoneOn_Iic <;> grind [MonotoneOn, StrictMonoOn] + · grind + exact A.trans B + · obtain ⟨b, hb⟩ : (s ∩ Iio a).Nonempty := by contrapose! h; simp [h] + have : Nonempty ((n : ℕ) × { u // StrictMonoOn u (Iic n) ∧ ∀ i ∈ Iic n, u i ∈ s ∩ Iio a }) := + ⟨0, ⟨fun i ↦ b, by grind [StrictMonoOn]⟩⟩ + rw [eVariationOn_eq_strictMonoOn, ENNReal.iSup_add] + apply iSup_le + rintro ⟨n, u, u_mono, u_mem⟩ + have : Tendsto (fun y ↦ ∑ i ∈ Finset.range n, + edist (f (u (i + 1))) (f (u i)) + edist (f a) (f y)) (𝓝[s ∩ Iio a] a) + (𝓝 (∑ i ∈ Finset.range n, edist (f (u (i + 1))) (f (u i)) + edist (f a) l)) := + (Tendsto.edist tendsto_const_nhds h'f).const_add _ + apply le_of_tendsto this + have : s ∩ Ioo (u n) a ∈ 𝓝[s ∩ Iio a] a := + inter_mem_nhdsWithin_inter self_mem_nhdsWithin (Ioo_mem_nhdsLT (by grind [StrictMonoOn])) + filter_upwards [this, self_mem_nhdsWithin] with y hy h'y + let v i := if i ≤ n then u i else if i = n + 1 then y else a + have A : ∑ i ∈ Finset.range n, edist (f (u (i + 1))) (f (u i)) + edist (f a) (f y) + ≤ ∑ i ∈ Finset.range (n + 2), edist (f (v (i + 1))) (f (v i)) := by + simp only [Finset.sum_range_succ, add_assoc] + gcongr with i h + · grind + · exact le_add_left (by grind) + have B : ∑ i ∈ Finset.range (n + 2), edist (f (v (i + 1))) (f (v i)) ≤ + eVariationOn f (s ∩ Iic a) := + sum_le_of_monotoneOn_Iic (by grind [MonotoneOn, StrictMonoOn]) (by grind) + exact A.trans B + +/-- The variation of a function on `Ici a` is the sum of the variation on `Ioi a` and the +contribution of `a`, i.e., the distance between the right limit and the value at `a`. +We give a version relative to a set `s`. -/ +theorem eVariationOn_on_inter_Ici_eq_Ioi_add_edist + [TopologicalSpace α] [OrderTopology α] {f : α → E} {s : Set α} {a : α} {l : E} + (h : (𝓝[s ∩ Ioi a] a).NeBot) (ha : a ∈ s) + (h'f : Tendsto f (𝓝[s ∩ Ioi a] a) (𝓝 l)) : + eVariationOn f (s ∩ Ici a) = eVariationOn f (s ∩ Ioi a) + edist (f a) l := by + rw [← comp_ofDual f, ← comp_ofDual f] + exact eVariationOn_on_inter_Iic_eq_Iio_add_edist h ha h'f + /-- If a function is continuous on the left at a point `a`, then its variations on `Iio a` and on `Iic a` coincide. We give a version relative to a set `s`. -/ lemma eVariationOn_inter_Iio_eq_inter_Iic_of_continuousWithinAt [TopologicalSpace α] [OrderTopology α] {f : α → E} {s : Set α} {a : α} (h : (𝓝[s ∩ Iio a] a).NeBot) (h' : ContinuousWithinAt f (s ∩ Iic a) a) : eVariationOn f (s ∩ Iio a) = eVariationOn f (s ∩ Iic a) := by - apply le_antisymm (eVariationOn.mono _ (by grind)) - rw [eVariationOn_eq_strictMonoOn] - apply iSup_le - rintro ⟨n, u, u_mono, u_mem⟩ - have : u n ≤ a := (u_mem n (by simp)).2 - rcases this.eq_or_lt with hn | hn; swap - · exact sum_le_of_monotoneOn_Iic u_mono.monotoneOn (by grind [StrictMonoOn]) - cases n with - | zero => simp - | succ n => - simp only [Finset.range_add_one, Finset.mem_range, lt_self_iff_false, not_false_eq_true, - Finset.sum_insert, ge_iff_le] - have : Tendsto (fun b ↦ edist (f b) (f (u n)) - + ∑ i ∈ Finset.range n, edist (f (u (i + 1))) (f (u i))) (𝓝[s ∩ Iio a] a) - (𝓝 (edist (f (u (n + 1))) (f (u n)) - + ∑ i ∈ Finset.range n, edist (f (u (i + 1))) (f (u i)))) := by - apply Tendsto.add_const - apply Tendsto.edist _ tendsto_const_nhds - rw [hn] - apply h'.tendsto.mono_left - exact nhdsWithin_mono _ (by grind) - apply le_of_tendsto this - have : s ∩ Ioo (u n) (u (n + 1)) ∈ 𝓝[s ∩ Iio a] a := by - rw [hn] - apply inter_mem_nhdsWithin_inter self_mem_nhdsWithin - exact Ioo_mem_nhdsLT (by grind [StrictMonoOn]) - filter_upwards [this] with b hb - let v i := if i ≤ n then u i else b - calc edist (f b) (f (u n)) + ∑ i ∈ Finset.range n, edist (f (u (i + 1))) (f (u i)) - _ = ∑ i ∈ Finset.range (n + 1), edist (f (v (i + 1))) (f (v i)) := by - simp only [Finset.range_add_one, Finset.mem_range, lt_self_iff_false, not_false_eq_true, - Finset.sum_insert] - congr 1 <;> grind [Finset.sum_congr] - _ ≤ eVariationOn f (s ∩ Iio a) := - sum_le_of_monotoneOn_Iic (by grind [MonotoneOn, StrictMonoOn]) (by grind [StrictMonoOn]) + by_cases ha : a ∈ s + · have : Tendsto f (𝓝[s ∩ Iio a] a) (𝓝 (f a)) := h'.mono (by grind) + simp [eVariationOn_on_inter_Iic_eq_Iio_add_edist h ha this] + · congr 1 + grind /-- If a function is continuous on the right at a point `a`, then its variations on `Ioi a` and on `Ici a` coincide. We give a version relative to a set `s`. -/ @@ -556,6 +628,41 @@ lemma eVariationOn_inter_Ioi_eq_inter_Ici_of_continuousWithinAt rw [← comp_ofDual f, ← comp_ofDual f] exact eVariationOn_inter_Iio_eq_inter_Iic_of_continuousWithinAt h h' +lemma eVariationOn_Ioc_eq_Icc_of_continuousWithinAt' + [TopologicalSpace α] [OrderTopology α] {f : α → E} {a b : α} + [h : (𝓝[>] a).NeBot] (h' : ContinuousWithinAt f (Ici a) a) : + eVariationOn f (Ioc a b) = eVariationOn f (Icc a b) := by + rcases le_or_gt b a with hab | hab + · simp [hab] + have : (𝓝[Iic b ∩ Ioi a] a).NeBot := by + convert h using 1 + exact nhdsWithin_inter_of_mem (mem_nhdsWithin_of_mem_nhds (Iic_mem_nhds hab)) + convert eVariationOn_inter_Ioi_eq_inter_Ici_of_continuousWithinAt this + (h'.mono inter_subset_right) <;> grind + +lemma eVariationOn_Ioc_eq_Icc_of_continuousWithinAt + [TopologicalSpace α] [OrderTopology α] [DenselyOrdered α] {f : α → E} {a b : α} + (h' : ContinuousWithinAt f (Ici a) a) : + eVariationOn f (Ioc a b) = eVariationOn f (Icc a b) := by + rcases le_or_gt b a with hab | hab + · simp [hab] + have : (𝓝[Ioi a] a).NeBot := nhdsGT_neBot_of_exists_gt ⟨b, hab⟩ + exact eVariationOn_Ioc_eq_Icc_of_continuousWithinAt' h' + +lemma eVariationOn_Ico_eq_Icc_of_continuousWithinAt' + [TopologicalSpace α] [OrderTopology α] {f : α → E} {a b : α} + [h : (𝓝[<] a).NeBot] (h' : ContinuousWithinAt f (Iic a) a) : + eVariationOn f (Ico b a) = eVariationOn f (Icc b a) := by + rw [← comp_ofDual f, ← comp_ofDual f, ← Ioc_toDual, ← Icc_toDual] + exact eVariationOn_Ioc_eq_Icc_of_continuousWithinAt' h' + +lemma eVariationOn_Ico_eq_Icc_of_continuousWithinAt + [TopologicalSpace α] [OrderTopology α] [DenselyOrdered α] {f : α → E} {a b : α} + (h' : ContinuousWithinAt f (Iic a) a) : + eVariationOn f (Ico b a) = eVariationOn f (Icc b a) := by + rw [← comp_ofDual f, ← comp_ofDual f, ← Ioc_toDual, ← Icc_toDual] + exact eVariationOn_Ioc_eq_Icc_of_continuousWithinAt h' + lemma exists_lt_eVariationOn_inter_Icc {f : α → E} {ε : ℝ≥0∞} {s : Set α} (h : ε < eVariationOn f s) : ∃ a ∈ s, ∃ b ∈ s, a < b ∧ ε < eVariationOn f (s ∩ Icc a b) := by obtain ⟨n, u, ⟨u_mono, u_mem⟩, hu⟩ : ∃ n u, (Monotone u ∧ ∀ (i : ℕ), u i ∈ s) ∧ @@ -683,6 +790,134 @@ theorem _root_.BoundedVariationOn.tendsto_eVariationOn_Ioc_zero [TopologicalSpac rw [this] exact hf.ofDual.tendsto_eVariationOn_Ico_zero (toDual x) +/-- A bounded variation function has a limit on its left within a set. -/ +theorem _root_.BoundedVariationOn.exists_tendsto_left [CompleteSpace E] [TopologicalSpace α] + [OrderTopology α] {f : α → E} {s : Set α} (hf : BoundedVariationOn f s) (x : α) : + ∃ l, Tendsto f (𝓝[s ∩ Iio x] x) (𝓝 l) := by + rcases eq_empty_or_nonempty (s ∩ Iio x) with hs | hs + · simp only [hs, nhdsWithin_empty, tendsto_bot, exists_const_iff, and_true] + exact ⟨f x⟩ + exact BoundedVariationOn.exists_tendsto_left_of_filter (s := s ∩ Iio x) + (hf.mono inter_subset_left) _ (fun y hy ↦ inter_mem_nhdsWithin _ (Ici_mem_nhds hy.2)) hs + +/-- A bounded variation function has a limit on its right within a set. -/ +theorem _root_.BoundedVariationOn.exists_tendsto_right [CompleteSpace E] [TopologicalSpace α] + [OrderTopology α] {f : α → E} {s : Set α} (hf : BoundedVariationOn f s) (x : α) : + ∃ l, Tendsto f (𝓝[s ∩ Ioi x] x) (𝓝 l) := + hf.ofDual.exists_tendsto_left (toDual x) + +/-- A bounded variation function tends to its left-limit on its left. -/ +theorem _root_.BoundedVariationOn.tendsto_leftLim [CompleteSpace E] [TopologicalSpace α] + [OrderTopology α] {f : α → E} (hf : BoundedVariationOn f univ) (x : α) : + Tendsto f (𝓝[<] x) (𝓝 (f.leftLim x)) := by + apply tendsto_leftLim_of_tendsto + convert! hf.exists_tendsto_left x + simp + +/-- A bounded variation function tends to its right-limit on its right. -/ +theorem _root_.BoundedVariationOn.tendsto_rightLim [CompleteSpace E] [TopologicalSpace α] + [OrderTopology α] {f : α → E} (hf : BoundedVariationOn f univ) (x : α) : + Tendsto f (𝓝[>] x) (𝓝 (f.rightLim x)) := + hf.ofDual.tendsto_leftLim x + +theorem _root_.BoundedVariationOn.eVariationOn_Iic_eq_Iio_add_edist [CompleteSpace E] + [DenselyOrdered α] {f : α → E} {a : α} (hf : BoundedVariationOn f univ) : + eVariationOn f (Iic a) = eVariationOn f (Iio a) + edist (f a) (f.leftLim a) := by + let : TopologicalSpace α := Preorder.topology α + have : OrderTopology α := ⟨rfl⟩ + by_cases ha : IsBot a + · have A : Iic a = {a} := by ext x; grind [ha x] + have B : Iio a = ∅ := by simp [ha.isMin] + simp [A, B, leftLim_eq_of_isBot ha] + have : (𝓝[<] a).NeBot := nhdsLT_neBot_of_exists_lt (by simpa [IsBot] using ha) + have : eVariationOn f (univ ∩ Iic a) = eVariationOn f (univ ∩ Iio a) + + edist (f a) (f.leftLim a) := by + apply eVariationOn_on_inter_Iic_eq_Iio_add_edist (by simpa) (mem_univ _) + simpa only [univ_inter] using hf.tendsto_leftLim _ + simpa using this + +theorem _root_.BoundedVariationOn.eVariationOn_Ici_eq_Ioi_add_edist [CompleteSpace E] + [DenselyOrdered α] {f : α → E} {a : α} (hf : BoundedVariationOn f univ) : + eVariationOn f (Ici a) = eVariationOn f (Ioi a) + edist (f a) (f.rightLim a) := by + rw [← eVariationOn.comp_ofDual f, ← eVariationOn.comp_ofDual f] + exact hf.ofDual.eVariationOn_Iic_eq_Iio_add_edist (a := toDual a) + +/-- If a function has bounded variation, then the variation on +small closed intervals to the left of this point tends to the contribution of the point, i.e., +the distance between the left limit and the value at the point -/ +theorem _root_.BoundedVariationOn.tendsto_eVariationOn_Icc_left + [TopologicalSpace α] [OrderTopology α] {f : α → E} {s : Set α} {l : E} + (hf : BoundedVariationOn f s) {x : α} (h'f : Tendsto f (𝓝[s ∩ Iio x] x) (𝓝 l)) (hx : x ∈ s) : + Tendsto (fun y ↦ eVariationOn f (s ∩ Icc y x)) (𝓝[s ∩ Iio x] x) (𝓝 (edist (f x) l)) := by + rcases eq_or_neBot (𝓝[s ∩ Iio x] x) with h | h + · simp [h] + suffices H : Tendsto (fun y ↦ eVariationOn f (s ∩ Ico y x) + edist (f x) l) + (𝓝[s ∩ Iio x] x) (𝓝 (0 + edist (f x) l)) by + simp only [zero_add] at H + apply Tendsto.congr' _ H + filter_upwards [self_mem_nhdsWithin] with y hy + have N : 𝓝[s ∩ Ici y ∩ Iio x] x = 𝓝[s ∩ Iio x] x := by + rw [show s ∩ Ici y ∩ Iio x = s ∩ Iio x ∩ Ici y by grind, nhdsWithin_inter_of_mem'] + exact mem_nhdsWithin_of_mem_nhds (Ici_mem_nhds hy.2) + rw [show s ∩ Icc y x = (s ∩ Ici y) ∩ Iic x by grind, + eVariationOn_on_inter_Iic_eq_Iio_add_edist (l := l)] + · congr 2; grind + · convert h using 1 + · exact ⟨hx, hy.2.le⟩ + · convert h'f + apply Tendsto.add ?_ tendsto_const_nhds + exact (hf.tendsto_eVariationOn_Ico_zero x).mono_left (nhdsWithin_mono _ inter_subset_left) + +/-- If a function has bounded variation, then the variation on +small closed intervals to the right of this point tends to the contribution of the point, i.e., +the distance between the right limit and the value at the point -/ +theorem _root_.BoundedVariationOn.tendsto_eVariationOn_Icc_right + [TopologicalSpace α] [OrderTopology α] {f : α → E} {s : Set α} {l : E} + (hf : BoundedVariationOn f s) {x : α} (h'f : Tendsto f (𝓝[s ∩ Ioi x] x) (𝓝 l)) (hx : x ∈ s) : + Tendsto (fun y ↦ eVariationOn f (s ∩ Icc x y)) (𝓝[s ∩ Ioi x] x) (𝓝 (edist (f x) l)) := by + have : (fun y ↦ eVariationOn f (s ∩ Icc x y)) = + (fun y ↦ eVariationOn (f ∘ ofDual) (ofDual ⁻¹' s ∩ Icc (toDual y) (toDual x))) := by + ext y + rw [Icc_toDual, ← preimage_inter, comp_ofDual] + rw [this] + exact hf.ofDual.tendsto_eVariationOn_Icc_left h'f hx + +/-- If a function has locally bounded variation, then the variation on +small closed intervals to the left of this point tends to the contribution of the point, i.e., +the distance between the left limit and the value at the point -/ +theorem _root_.LocallyBoundedVariationOn.tendsto_eVariationOn_Icc_left + [TopologicalSpace α] [OrderTopology α] {f : α → E} {s : Set α} {l : E} + (hf : LocallyBoundedVariationOn f s) {x : α} + (h'f : Tendsto f (𝓝[s ∩ Iio x] x) (𝓝 l)) (hx : x ∈ s) : + Tendsto (fun y ↦ eVariationOn f (s ∩ Icc y x)) (𝓝[s ∩ Iio x] x) (𝓝 (edist (f x) l)) := by + rcases eq_or_neBot (𝓝[s ∩ Iio x] x) with h | h + · simp [h] + obtain ⟨y, hy⟩ : (s ∩ Iio x).Nonempty := by contrapose! h; simp [h] + have : 𝓝[s ∩ Iio x] x = 𝓝[(s ∩ Icc y x) ∩ Iio x] x := by + rw [show (s ∩ Icc y x) ∩ Iio x = (s ∩ Iio x) ∩ Icc y x by grind, eq_comm] + apply nhdsWithin_inter_of_mem' (nhdsWithin_mono _ inter_subset_right (Icc_mem_nhdsLT hy.2)) + rw [this] at h'f ⊢ + have : BoundedVariationOn f (s ∩ Icc y x) := hf _ _ hy.1 hx + apply Tendsto.congr' _ (this.tendsto_eVariationOn_Icc_left h'f ⟨hx, by grind⟩) + filter_upwards [self_mem_nhdsWithin] with z hz + congr 1 + grind + +/-- If a function has locally bounded variation, then the variation on +small closed intervals to the right of this point tends to the contribution of the point, i.e., +the distance between the right limit and the value at the point -/ +theorem _root_.LocallyBoundedVariationOn.tendsto_eVariationOn_Icc_right + [TopologicalSpace α] [OrderTopology α] {f : α → E} {s : Set α} {l : E} + (hf : LocallyBoundedVariationOn f s) {x : α} + (h'f : Tendsto f (𝓝[s ∩ Ioi x] x) (𝓝 l)) (hx : x ∈ s) : + Tendsto (fun y ↦ eVariationOn f (s ∩ Icc x y)) (𝓝[s ∩ Ioi x] x) (𝓝 (edist (f x) l)) := by + have : (fun y ↦ eVariationOn f (s ∩ Icc x y)) = + (fun y ↦ eVariationOn (f ∘ ofDual) (ofDual ⁻¹' s ∩ Icc (toDual y) (toDual x))) := by + ext y + rw [Icc_toDual, ← preimage_inter, comp_ofDual] + rw [this] + exact hf.ofDual.tendsto_eVariationOn_Icc_left h'f hx + /-- If a function has bounded variation and is left-continuous at a point, then the variation on small closed intervals to the left of this point tends to `0`. -/ theorem _root_.BoundedVariationOn.tendsto_eVariationOn_Icc_zero_left @@ -721,36 +956,6 @@ theorem _root_.BoundedVariationOn.tendsto_eVariationOn_Icc_zero_right rw [this] exact hf.ofDual.tendsto_eVariationOn_Icc_zero_left h -/-- A bounded variation function has a limit on its left within a set. -/ -theorem _root_.BoundedVariationOn.exists_tendsto_left [CompleteSpace E] [TopologicalSpace α] - [OrderTopology α] {f : α → E} {s : Set α} (hf : BoundedVariationOn f s) (x : α) : - ∃ l, Tendsto f (𝓝[s ∩ Iio x] x) (𝓝 l) := by - rcases eq_empty_or_nonempty (s ∩ Iio x) with hs | hs - · simp only [hs, nhdsWithin_empty, tendsto_bot, exists_const_iff, and_true] - exact ⟨f x⟩ - exact BoundedVariationOn.exists_tendsto_left_of_filter (s := s ∩ Iio x) - (hf.mono inter_subset_left) _ (fun y hy ↦ inter_mem_nhdsWithin _ (Ici_mem_nhds hy.2)) hs - -/-- A bounded variation function has a limit on its right within a set. -/ -theorem _root_.BoundedVariationOn.exists_tendsto_right [CompleteSpace E] [TopologicalSpace α] - [OrderTopology α] {f : α → E} {s : Set α} (hf : BoundedVariationOn f s) (x : α) : - ∃ l, Tendsto f (𝓝[s ∩ Ioi x] x) (𝓝 l) := - hf.ofDual.exists_tendsto_left (toDual x) - -/-- A bounded variation function tends to its left-limit on its left. -/ -theorem _root_.BoundedVariationOn.tendsto_leftLim [CompleteSpace E] [TopologicalSpace α] - [OrderTopology α] {f : α → E} (hf : BoundedVariationOn f univ) (x : α) : - Tendsto f (𝓝[<] x) (𝓝 (f.leftLim x)) := by - apply tendsto_leftLim_of_tendsto - convert! hf.exists_tendsto_left x - simp - -/-- A bounded variation function tends to its right-limit on its right. -/ -theorem _root_.BoundedVariationOn.tendsto_rightLim [CompleteSpace E] [TopologicalSpace α] - [OrderTopology α] {f : α → E} (hf : BoundedVariationOn f univ) (x : α) : - Tendsto f (𝓝[>] x) (𝓝 (f.rightLim x)) := - hf.ofDual.tendsto_leftLim x - /-- If a function `g` is at each point `x` a limit of `f` to the left or to the right (or more generally a cluster point of the values of `f` around `x`) then the variation of `g` is bounded by that of `f`. -/ @@ -804,23 +1009,27 @@ private lemma eVariationOn_le_of_mapClusterPt grind exact sum_le_of_monotoneOn_Iic v_mono.monotoneOn (by grind) -lemma eVariationOn_leftLim_le [TopologicalSpace α] [OrderTopology α] {f : α → E} : - eVariationOn f.leftLim univ ≤ eVariationOn f univ := by +lemma eVariationOn_leftLim_le [TopologicalSpace α] [OrderTopology α] {f : α → E} + {s : Set α} (hs : IsOpen s) : + eVariationOn f.leftLim s ≤ eVariationOn f s := by apply eVariationOn_le_of_mapClusterPt (fun x hx ↦ ?_) - apply (mapClusterPt_leftLim f x).mono (nhdsWithin_mono _ (subset_univ _)) + rw [IsOpen.nhdsWithin_eq hs hx] + exact (mapClusterPt_leftLim f x).mono nhdsWithin_le_nhds -lemma eVariationOn_rightLim_le [TopologicalSpace α] [OrderTopology α] {f : α → E} : - eVariationOn f.rightLim univ ≤ eVariationOn f univ := by +lemma eVariationOn_rightLim_le [TopologicalSpace α] [OrderTopology α] {f : α → E} + {s : Set α} (hs : IsOpen s) : + eVariationOn f.rightLim s ≤ eVariationOn f s := by apply eVariationOn_le_of_mapClusterPt (fun x hx ↦ ?_) - apply (mapClusterPt_rightLim f x).mono (nhdsWithin_mono _ (subset_univ _)) + rw [IsOpen.nhdsWithin_eq hs hx] + exact (mapClusterPt_rightLim f x).mono nhdsWithin_le_nhds lemma _root_.BoundedVariationOn.leftLim [TopologicalSpace α] [OrderTopology α] {f : α → E} (hf : BoundedVariationOn f univ) : BoundedVariationOn f.leftLim univ := - (eVariationOn_leftLim_le.trans_lt hf.lt_top).ne + ((eVariationOn_leftLim_le isOpen_univ).trans_lt hf.lt_top).ne lemma _root_.BoundedVariationOn.rightLim [TopologicalSpace α] [OrderTopology α] {f : α → E} (hf : BoundedVariationOn f univ) : BoundedVariationOn f.rightLim univ := - (eVariationOn_rightLim_le.trans_lt hf.lt_top).ne + ((eVariationOn_rightLim_le isOpen_univ).trans_lt hf.lt_top).ne lemma _root_.BoundedVariationOn.continuousWithinAt_leftLim [TopologicalSpace α] [OrderTopology α] [CompleteSpace E] [T3Space E] {f : α → E} (hf : BoundedVariationOn f univ) {x : α} : @@ -909,6 +1118,17 @@ theorem MonotoneOn.locallyBoundedVariationOn {f : α → ℝ} {s : Set α} (hf : LocallyBoundedVariationOn f s := fun _ _ as bs => ((hf.eVariationOn_le as bs).trans_lt ENNReal.ofReal_lt_top).ne +theorem MonotoneOn.boundedVariationOn + {f : α → ℝ} {s : Set α} {C : ℝ} (hf : MonotoneOn f s) (h : ∀ x ∈ s, |f x| ≤ C) : + BoundedVariationOn f s := by + suffices eVariationOn f s ≤ ENNReal.ofReal (2 * C) from + ne_of_lt (this.trans_lt (by simp [ENNReal.mul_lt_top])) + rw [eVariationOn.eq_biSup_inter_Icc] + simp only [mem_setOf_eq, iSup_le_iff, and_imp, Prod.forall] + intro a b as bs hab + grw [hf.eVariationOn_le as bs] + exact ENNReal.ofReal_mono (by grind) + /-- The **signed** variation of `f` on the interval `Icc a b` intersected with the set `s`, squashed to a real (therefore only really meaningful if the variation is finite) -/ @@ -967,6 +1187,16 @@ protected theorem add {f : α → E} {s : Set α} (hf : LocallyBoundedVariationO variationOnFromTo.eq_of_le f s (xy.trans yz), ← ENNReal.toReal_add (hf x y xs ys) (hf y z ys zs), eVariationOn.Icc_add_Icc f xy yz ys] +protected theorem sub_right {f : α → E} {s : Set α} (hf : LocallyBoundedVariationOn f s) {a b c : α} + (ha : a ∈ s) (hb : b ∈ s) (hc : c ∈ s) : + variationOnFromTo f s a b - variationOnFromTo f s a c = variationOnFromTo f s c b := by + rw [← variationOnFromTo.add hf ha hc hb, add_sub_cancel_left] + +protected theorem sub_left {f : α → E} {s : Set α} (hf : LocallyBoundedVariationOn f s) {a b c : α} + (ha : a ∈ s) (hb : b ∈ s) (hc : c ∈ s) : + variationOnFromTo f s a b - variationOnFromTo f s c b = variationOnFromTo f s a c := by + rw [← variationOnFromTo.add hf ha hc hb, add_sub_cancel_right] + variable {f s} in protected theorem edist_zero_of_eq_zero (hf : LocallyBoundedVariationOn f s) {a b : α} (ha : a ∈ s) (hb : b ∈ s) (h : variationOnFromTo f s a b = 0) : @@ -1061,6 +1291,78 @@ protected theorem comp_eq_of_monotoneOn {β : Type*} [LinearOrder β] (f : α · rw [variationOnFromTo.eq_of_ge _ _ h, variationOnFromTo.eq_of_ge _ _ (hφ hy hx h), eVariationOn.comp_inter_Icc_eq_of_monotoneOn f φ hφ hy hx] +/-- The jump of `variationOnFromTo` on the left of a point is given by the distance between the +left limit and the value of the function. -/ +theorem tendsto_left {E : Type*} [PseudoMetricSpace E] [TopologicalSpace α] [OrderTopology α] + {f : α → E} {l : E} {a b : α} (ha : a ∈ s) (hb : b ∈ s) + (hf : LocallyBoundedVariationOn f s) (h'f : Tendsto f (𝓝[s ∩ Iio b] b) (𝓝 l)) : + Tendsto (variationOnFromTo f s a) (𝓝[s ∩ Iio b] b) + (𝓝 (variationOnFromTo f s a b - dist (f b) l)) := by + suffices H : Tendsto (fun x ↦ variationOnFromTo f s a b - variationOnFromTo f s x b) + (𝓝[s ∩ Iio b] b) (𝓝 (variationOnFromTo f s a b - dist (f b) l)) by + apply Tendsto.congr' _ H + filter_upwards [self_mem_nhdsWithin] with x hx + rw [variationOnFromTo.sub_left hf ha hb hx.1] + apply Tendsto.const_sub + suffices H : Tendsto (fun x ↦ (eVariationOn f (s ∩ Icc x b)).toReal) (𝓝[s ∩ Iio b] b) + (𝓝 (dist (f b) l)) by + apply Tendsto.congr' _ H + filter_upwards [self_mem_nhdsWithin] with x hx using by simp [variationOnFromTo, hx.2.le] + rw [dist_edist] + exact (ENNReal.tendsto_toReal (by simp)).comp (hf.tendsto_eVariationOn_Icc_left h'f hb) + +/-- The jump of `variationOnFromTo` on the right of a point is given by the distance between the +right limit and the value of the function. -/ +theorem tendsto_right {E : Type*} [PseudoMetricSpace E] [TopologicalSpace α] [OrderTopology α] + {f : α → E} {l : E} {a b : α} (ha : a ∈ s) (hb : b ∈ s) + (hf : LocallyBoundedVariationOn f s) (h'f : Tendsto f (𝓝[s ∩ Ioi b] b) (𝓝 l)) : + Tendsto (variationOnFromTo f s a) (𝓝[s ∩ Ioi b] b) + (𝓝 (variationOnFromTo f s a b + dist (f b) l)) := by + suffices H : Tendsto (fun x ↦ variationOnFromTo f s a b + variationOnFromTo f s b x) + (𝓝[s ∩ Ioi b] b) (𝓝 (variationOnFromTo f s a b + dist (f b) l)) by + apply Tendsto.congr' _ H + filter_upwards [self_mem_nhdsWithin] with x hx + rw [variationOnFromTo.add hf ha hb hx.1] + apply Tendsto.const_add + suffices H : Tendsto (fun x ↦ (eVariationOn f (s ∩ Icc b x)).toReal) (𝓝[s ∩ Ioi b] b) + (𝓝 (dist (f b) l)) by + apply Tendsto.congr' _ H + filter_upwards [self_mem_nhdsWithin] with x hx using by simp [variationOnFromTo, hx.2.le] + rw [dist_edist] + exact (ENNReal.tendsto_toReal (by simp)).comp (hf.tendsto_eVariationOn_Icc_right h'f hb) + +/-- The jump of `variationOnFromTo` on the left of a point is given by the distance between the +left limit and the value of the function. -/ +theorem leftLim_eq {E : Type*} [PseudoMetricSpace E] [CompleteSpace E] + {f : α → E} {a b : α} (hf : BoundedVariationOn f univ) : + (variationOnFromTo f univ a).leftLim b = + variationOnFromTo f univ a b - dist (f b) (f.leftLim b) := by + let : TopologicalSpace α := Preorder.topology α + have : OrderTopology α := ⟨rfl⟩ + rcases eq_or_neBot (𝓝[<] b) with hb | hb + · simp [leftLim_eq_of_eq_bot _ hb] + apply leftLim_eq_of_tendsto + have := variationOnFromTo.tendsto_left (f := f) (l := f.leftLim b) (mem_univ a) (mem_univ b) + hf.locallyBoundedVariationOn + simp only [univ_inter] at this + exact this (hf.tendsto_leftLim _) + +/-- The jump of `variationOnFromTo` on the right of a point is given by the distance between the +right limit and the value of the function. -/ +theorem rightLim_eq {E : Type*} [PseudoMetricSpace E] [CompleteSpace E] + {f : α → E} {a b : α} (hf : BoundedVariationOn f univ) : + (variationOnFromTo f univ a).rightLim b = + variationOnFromTo f univ a b + dist (f b) (f.rightLim b) := by + let : TopologicalSpace α := Preorder.topology α + have : OrderTopology α := ⟨rfl⟩ + rcases eq_or_neBot (𝓝[>] b) with hb | hb + · simp [rightLim_eq_of_eq_bot _ hb] + apply rightLim_eq_of_tendsto + have := variationOnFromTo.tendsto_right (f := f) (l := f.rightLim b) (mem_univ a) (mem_univ b) + hf.locallyBoundedVariationOn + simp only [univ_inter] at this + exact this (hf.tendsto_rightLim _) + theorem _root_.BoundedVariationOn.continuousWithinAt_variationOnFromTo_Ici [TopologicalSpace α] [OrderTopology α] (hf : BoundedVariationOn f univ) {a x : α} (hx : ContinuousWithinAt f (Ici x) x) : diff --git a/Mathlib/Topology/EMetricSpace/Defs.lean b/Mathlib/Topology/EMetricSpace/Defs.lean index 44fa6e9c5e5d8f..b9dbd634ed6d89 100644 --- a/Mathlib/Topology/EMetricSpace/Defs.lean +++ b/Mathlib/Topology/EMetricSpace/Defs.lean @@ -181,7 +181,7 @@ theorem mem_uniformity_edist {s : Set (α × α)} : /-- Make a `PseudoEMetricSpace` from a metric. Warning: the uniformity and topology included herein are the ones generated by the metric. If the type has a pre-existing topology or uniformity, `PseudoEMetricSpace.ofEDistOfTopology` should be used instead. -/ - noncomputable abbrev PseudoEMetricSpace.ofEDist +noncomputable abbrev PseudoEMetricSpace.ofEDist {α : Type u} (edist : α → α → ℝ≥0∞) (edist_self : ∀ x : α, edist x x = 0) (edist_comm : ∀ x y : α, edist x y = edist y x) (edist_triangle : ∀ x y z : α, edist x z ≤ edist x y + edist y z) : PseudoEMetricSpace α where diff --git a/Mathlib/Topology/FiberBundle/Constructions.lean b/Mathlib/Topology/FiberBundle/Constructions.lean index 0e44b40be93d8c..98b4083007ac0f 100644 --- a/Mathlib/Topology/FiberBundle/Constructions.lean +++ b/Mathlib/Topology/FiberBundle/Constructions.lean @@ -301,7 +301,7 @@ theorem Pullback.continuous_totalSpaceMk [∀ x, TopologicalSpace (E x)] [FiberB exact (FiberBundle.totalSpaceMk_isInducing F E (f x)).eq_induced.le variable {E F} -variable [∀ _b, Zero (E _b)] {K : Type U} [FunLike K B' B] [ContinuousMapClass K B' B] +variable [∀ _b, Nonempty (E _b)] {K : Type U} [FunLike K B' B] [ContinuousMapClass K B' B] /-- A fiber bundle trivialization can be pulled back to a trivialization on the pullback bundle. -/ @[simps] diff --git a/Mathlib/Topology/FiberBundle/Trivialization.lean b/Mathlib/Topology/FiberBundle/Trivialization.lean index 38133c78000fab..2177900ac23584 100644 --- a/Mathlib/Topology/FiberBundle/Trivialization.lean +++ b/Mathlib/Topology/FiberBundle/Trivialization.lean @@ -223,29 +223,38 @@ theorem symm_coe_proj {x : B} {y : F} (e' : Pretrivialization F (π F E)) (h : x (e'.toPartialEquiv.symm (x, y)).1 = x := e'.proj_symm_apply' h -section Zero +section Nonempty -variable [∀ x, Zero (E x)] +variable [∀ x, Nonempty (E x)] open Classical in /-- A fiberwise inverse to `e`. This is the function `F → E b` that induces a local inverse -`B × F → TotalSpace F E` of `e` on `e.baseSet`. It is defined to be `0` outside `e.baseSet`. -/ +`B × F → TotalSpace F E` of `e` on `e.baseSet`. Outside of `e.baseSet` it takes on arbitrarily +chosen junk values. -/ protected noncomputable def symm (e : Pretrivialization F (π F E)) (b : B) (y : F) : E b := if hb : b ∈ e.baseSet then cast (congr_arg E (e.proj_symm_apply' hb)) (e.toPartialEquiv.symm (b, y)).2 - else 0 + else Classical.arbitrary _ theorem symm_apply (e : Pretrivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet) (y : F) : e.symm b y = cast (congr_arg E (e.symm_coe_proj hb)) (e.toPartialEquiv.symm (b, y)).2 := dif_pos hb +@[deprecated "The junk values of `Pretrivialization.symm` were changed from `0` to +`Classical.arbitrary` and should not be relied on; this lemma will be removed soon. Note that this +change does not affect the linear versions `symmₗ` and `symmL`, which still retain `0` as the junk +values." (since := "2026-06-23")] theorem symm_apply_of_notMem (e : Pretrivialization F (π F E)) {b : B} (hb : b ∉ e.baseSet) - (y : F) : e.symm b y = 0 := - dif_neg hb + (y : F) : e.symm b y = Classical.arbitrary _ := by + simp [Pretrivialization.symm, hb] +@[deprecated "The junk values of `Pretrivialization.symm` were changed from `0` to +`Classical.arbitrary` and should not be relied on; this lemma will be removed soon. Note that this +change does not affect the linear versions `symmₗ` and `symmL`, which still retain `0` as the junk +values." (since := "2026-06-23")] theorem coe_symm_of_notMem (e : Pretrivialization F (π F E)) {b : B} (hb : b ∉ e.baseSet) : - (e.symm b : F → E b) = 0 := - funext fun _ => dif_neg hb + e.symm b = fun _ ↦ Classical.arbitrary _ := by + ext; exact symm_apply_of_notMem e hb _ theorem mk_symm (e : Pretrivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet) (y : F) : TotalSpace.mk b (e.symm b y) = e.toPartialEquiv.symm (b, y) := by @@ -266,7 +275,7 @@ theorem apply_mk_symm (e : Pretrivialization F (π F E)) {b : B} (hb : b ∈ e.b e ⟨b, e.symm b y⟩ = (b, y) := by rw [e.mk_symm hb, e.apply_symm_apply (e.mk_mem_target.mpr hb)] -end Zero +end Nonempty /-- The restriction of a pretrivialization to a subset of the base. -/ @[simps toFun source target baseSet] @@ -668,12 +677,13 @@ theorem symm_coe_proj {x : B} {y : F} (e : Trivialization F (π F E)) (h : x ∈ (e.toOpenPartialHomeomorph.symm (x, y)).1 = x := e.proj_symm_apply' h -section Zero +section Nonempty -variable [∀ x, Zero (E x)] +variable [∀ x, Nonempty (E x)] /-- A fiberwise inverse to `e'`. The function `F → E x` that induces a local inverse -`B × F → TotalSpace F E` of `e'` on `e'.baseSet`. It is defined to be `0` outside `e'.baseSet`. -/ +`B × F → TotalSpace F E` of `e'` on `e'.baseSet`. It takes on junk values chosen using +`Classical.arbitrary` outside `e'.baseSet`. -/ protected noncomputable def symm (e : Trivialization F (π F E)) (b : B) (y : F) : E b := e.toPretrivialization.symm b y @@ -682,9 +692,13 @@ theorem symm_apply (e : Trivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet cast (congr_arg E (e.symm_coe_proj hb)) (e.toOpenPartialHomeomorph.symm (b, y)).2 := dif_pos hb +@[deprecated "The junk values of `Trivialization.symm` were changed from `0` to +`Classical.arbitrary` and should not be relied on; this lemma will be removed soon. Note that this +change does not affect the linear versions `symmₗ` and `symmL`, which still retain `0` as the junk +values." (since := "2026-06-23")] theorem symm_apply_of_notMem (e : Trivialization F (π F E)) {b : B} (hb : b ∉ e.baseSet) (y : F) : - e.symm b y = 0 := - dif_neg hb + e.symm b y = Classical.arbitrary _ := + e.toPretrivialization.symm_apply_of_notMem hb y theorem mk_symm (e : Trivialization F (π F E)) {b : B} (hb : b ∈ e.baseSet) (y : F) : TotalSpace.mk b (e.symm b y) = e.toOpenPartialHomeomorph.symm (b, y) := @@ -715,7 +729,7 @@ theorem continuousOn_symm (e : Trivialization F (π F E)) : rw [← e.target_eq] exact e.toOpenPartialHomeomorph.continuousOn_symm -end Zero +end Nonempty /-- If `e` is a `Trivialization` of `proj : Z → B` with fiber `F` and `h` is a homeomorphism `F ≃ₜ F'`, then `e.trans_fiber_homeomorph h` is the trivialization of `proj` with the fiber `F'` diff --git a/Mathlib/Topology/Gluing.lean b/Mathlib/Topology/Gluing.lean index 2e21faac2ffbe4..136fdc6e0b5abe 100644 --- a/Mathlib/Topology/Gluing.lean +++ b/Mathlib/Topology/Gluing.lean @@ -184,7 +184,7 @@ theorem ι_eq_iff_rel (i j : D.J) (x : D.U i) (y : D.U j) : show _ = Sigma.mk j y from ConcreteCategory.congr_hom (sigmaIsoSigma.{_, u} D.U).inv_hom_id _] change InvImage D.Rel (sigmaIsoSigma.{_, u} D.U).hom _ _ rw [← (InvImage.equivalence _ _ D.rel_equiv).eqvGen_iff] - refine Relation.EqvGen.mono ?_ (D.eqvGen_of_π_eq h :) + refine Relation.EqvGen.mono ?_ _ _ (D.eqvGen_of_π_eq h :) rintro _ _ ⟨x⟩ obtain ⟨⟨⟨i, j⟩, y⟩, rfl⟩ := (ConcreteCategory.bijective_of_isIso (sigmaIsoSigma.{u, u} _).inv).2 x diff --git a/Mathlib/Topology/Homeomorph/Defs.lean b/Mathlib/Topology/Homeomorph/Defs.lean index 79ccff73ceae30..2833bea0f96d53 100644 --- a/Mathlib/Topology/Homeomorph/Defs.lean +++ b/Mathlib/Topology/Homeomorph/Defs.lean @@ -6,7 +6,7 @@ Authors: Johannes Hölzl, Patrick Massot, Sébastien Gouëzel, Zhouhang Zhou, Re module public import Mathlib.Topology.ContinuousMap.Defs -public import Mathlib.Topology.Maps.Basic +public import Mathlib.Topology.Maps.OpenQuotient /-! # Homeomorphisms @@ -342,6 +342,20 @@ theorem comp_isOpenMap_iff' (h : X ≃ₜ Y) {f : Y → Z} : IsOpenMap (f ∘ h) rw [← Function.comp_id f, ← h.self_comp_symm, ← Function.comp_assoc] exact hf.comp h.symm.isOpenMap +/-- Open quotient maps are preserved by precomposing with a homeomorphism. -/ +@[simp] +theorem isOpenQuotient_comp_iff (e : X ≃ₜ Y) {f : Y → Z} : + IsOpenQuotientMap (f ∘ e) ↔ IsOpenQuotientMap f := + ⟨fun h ↦ by simpa [Function.comp_assoc] using h.comp e.symm.isOpenQuotientMap, + fun hf ↦ hf.comp e.isOpenQuotientMap⟩ + +/-- Open quotient maps are preserved by postcomposing with a homeomorphism. -/ +@[simp] +theorem comp_isOpenQuotientMap_iff (e : Y ≃ₜ Z) {f : X → Y} : + IsOpenQuotientMap (e ∘ f) ↔ IsOpenQuotientMap f := + ⟨fun h ↦ by simpa [← Function.comp_assoc] using e.symm.isOpenQuotientMap.comp h, + fun hf ↦ e.isOpenQuotientMap.comp hf⟩ + variable (X Y) in /-- If both `X` and `Y` have a unique element, then `X ≃ₜ Y`. -/ @[simps!] diff --git a/Mathlib/Topology/Homotopy/Lifting.lean b/Mathlib/Topology/Homotopy/Lifting.lean index 1178bed0e81c2b..d6bb44b3bfee66 100644 --- a/Mathlib/Topology/Homotopy/Lifting.lean +++ b/Mathlib/Topology/Homotopy/Lifting.lean @@ -7,8 +7,8 @@ module public import Mathlib.AlgebraicTopology.FundamentalGroupoid.FundamentalGroup public import Mathlib.AlgebraicTopology.FundamentalGroupoid.SimplyConnected -public import Mathlib.Topology.Connected.LocPathConnected -public import Mathlib.Topology.Covering.Basic +public import Mathlib.Topology.Connected.LocallyPathConnected +public import Mathlib.Topology.Covering.Quotient public import Mathlib.Topology.Homotopy.Path public import Mathlib.Topology.UnitInterval @@ -26,9 +26,11 @@ public import Mathlib.Topology.UnitInterval arbitrary point). -/ +noncomputable section + @[expose] public section -open Topology unitInterval +open Function Topology unitInterval variable {E X A : Type*} [TopologicalSpace E] [TopologicalSpace X] [TopologicalSpace A] {p : E → X} @@ -101,7 +103,7 @@ theorem exists_lift_nhds {f : C(I × A, X)} {g : I × A → E} (g_lifts : p ∘ rw [if_pos this] -- here we use that {tₙ} × Nₙ₊₁ is mapped to the domain of `q e` apply (q e).injOn (by rwa [← ta.eta, ht]) ((q e).map_target this) - rw [(q e).right_inv this, ← hpq e]; exact congr_fun g'_lifts ta + rw [(q e).right_inv this, ← hpq e]; exact congr($g'_lifts ta) · rw [closure_le_eq continuous_fst continuous_const] at ht exact ⟨⟨hta.1.1, ht⟩, hta.2.2.1⟩ · simp_rw [not_le]; exact (ContinuousOn.congr ((q e).continuousOn_invFun.comp f.2.continuousOn @@ -109,14 +111,14 @@ theorem exists_lift_nhds {f : C(I × A, X)} {g : I × A → E} (g_lifts : p ∘ fun _ h ↦ if_pos <| huv ⟨hu ⟨h.2, h.1.1.2⟩, h.1.2.1⟩).mono (Set.inter_subset_inter_right _ <| closure_lt_subset_le continuous_const continuous_fst) · ext ta; rw [Function.comp_apply]; split_ifs with _ hv - · exact congr_fun g'_lifts ta + · exact congr($g'_lifts ta) · rw [hpq e, (q e).right_inv hv] - · exact congr_fun g_lifts ta + · exact congr($g_lifts ta) · rw [← g'_0]; exact if_pos bot_le · dsimp only; split_ifs with htn hf · exact g'_a t0 htn · apply (q e).injOn ((q e).map_target hf) (h_sub ⟨le_of_not_ge htn, htn1⟩) - rw [(q e).right_inv hf, ← hpq e]; exact (congr_fun g_lifts _).symm + rw [(q e).right_inv hf, ← hpq e]; exact congr($g_lifts _).symm · rfl variable (sep : IsSeparatedMap p) @@ -166,7 +168,7 @@ open PathConnectedSpace (somePath) in path `f ∘ γ` in `X` lifts to `E` with endpoint only dependent on the endpoint of `γ` and independent of the path chosen. In this theorem, we require that a specific point `a₀ : A` is lifted to a specific point `e₀ : E` over `a₀`. -/ -theorem existsUnique_continuousMap_lifts [PathConnectedSpace A] [LocPathConnectedSpace A] +theorem existsUnique_continuousMap_lifts [PathConnectedSpace A] [LocallyPathConnectedSpace A] (f : C(A, X)) (a₀ : A) (e₀ : E) (he : p e₀ = f a₀) (ex : ∀ γ : C(I, A), γ 0 = a₀ → ∃ Γ : C(I, E), Γ 0 = e₀ ∧ p ∘ Γ = f.comp γ) (uniq : ∀ γ γ' : C(I, A), ∀ Γ Γ' : C(I, E), γ 0 = a₀ → γ' 0 = a₀ → Γ 0 = e₀ → Γ' 0 = e₀ → @@ -254,7 +256,7 @@ theorem exists_path_lifts : ∃ Γ : C(I, E), p ∘ Γ = γ ∧ Γ 0 = e := by · dsimp only; rwa [if_pos (t_0 ▸ t_mono n.zero_le)] /-- The lift of a path to a covering space given a lift of the left endpoint. -/ -noncomputable def liftPath : C(I, E) := (cov.exists_path_lifts γ e γ_0).choose +def liftPath : C(I, E) := (cov.exists_path_lifts γ e γ_0).choose lemma liftPath_lifts : p ∘ cov.liftPath γ e γ_0 = γ := (cov.exists_path_lifts γ e γ_0).choose_spec.1 lemma liftPath_zero : cov.liftPath γ e γ_0 0 = e := (cov.exists_path_lifts γ e γ_0).choose_spec.2 @@ -277,13 +279,13 @@ lemma liftPath_const {x : X} (hpe : x = p e) : cov.liftPath (.const I x) e hpe = lemma liftPath_trans {x y z : X} {e : E} (hpe : x = p e) (γ : Path x y) (γ' : Path y z) : letI Γ := cov.liftPath γ e (γ.source.trans hpe) cov.liftPath (γ.trans γ') e (by simpa) = (⟨Γ, liftPath_zero .., rfl⟩ : Path e (Γ 1)).trans - ⟨cov.liftPath γ' (Γ 1) (by simpa using (congr_fun (cov.liftPath_lifts γ ..) 1).symm), + ⟨cov.liftPath γ' (Γ 1) (by simpa using congr($(cov.liftPath_lifts γ ..) 1).symm), liftPath_zero .., rfl⟩ := by refine .symm <| (cov.eq_liftPath_iff' _).mpr ⟨funext fun _ ↦ ?_, by simp⟩ simp only [ContinuousMap.coe_coe, Function.comp_apply, Path.trans_apply]; split_ifs · exact congr_fun (cov.liftPath_lifts γ e (γ.source.trans hpe)) _ · refine congr_fun (cov.liftPath_lifts γ' _ ?_) _ - simpa using (congr_fun (cov.liftPath_lifts γ ..) 1).symm + simpa using congr($(cov.liftPath_lifts γ ..) 1).symm end path_lifting @@ -294,7 +296,7 @@ variable (H : C(I × A, X)) (f : C(A, E)) (H_0 : ∀ a, H (0, a) = p (f a)) the homotopy lifting property for covering maps. In other words, a covering map is a Hurewicz fibration. Proposition 1.30 of [hatcher02]. -/ -@[simps] noncomputable def liftHomotopy : C(I × A, E) where +@[simps] def liftHomotopy : C(I × A, E) where toFun ta := cov.liftPath (H.comp <| (ContinuousMap.id I).prodMk <| .const I ta.2) (f ta.2) (H_0 ta.2) ta.1 continuous_toFun := cov.isLocalHomeomorph.continuous_lift cov.isSeparatedMap H @@ -326,7 +328,7 @@ variable {f₀ f₁ : C(A, X)} {S : Set A} (F : f₀.HomotopyRel f₁ S) open ContinuousMap in /-- The lift to a covering space of a homotopy between two continuous maps relative to a set given compatible lifts of the continuous maps. -/ -noncomputable def liftHomotopyRel [PreconnectedSpace A] +def liftHomotopyRel [PreconnectedSpace A] {f₀' f₁' : C(A, E)} (he : ∃ a ∈ S, f₀' a = f₁' a) (h₀ : p ∘ f₀' = f₀) (h₁ : p ∘ f₁' = f₁) : f₀'.HomotopyRel f₁' S := have F_0 : ∀ a, F (0, a) = p (f₀' a) := fun a ↦ (F.apply_zero a).trans (congr_fun h₀ a).symm @@ -339,10 +341,10 @@ noncomputable def liftHomotopyRel [PreconnectedSpace A] map_zero_left := cov.liftHomotopy_zero F f₀' F_0 map_one_left := by obtain ⟨a, ha, he⟩ := he - simp_rw [toFun_eq_coe, ← curry_apply] + simp_rw [toFun_eq_coe, ← ContinuousMap.curry_apply] refine congr_fun (cov.eq_of_comp_eq (ContinuousMap.continuous _) f₁'.continuous ?_ a <| (rel 1 a ha).trans he) - ext a; rw [h₁, Function.comp_apply, curry_apply] + ext a; rw [h₁, Function.comp_apply, ContinuousMap.curry_apply] exact (congr_fun (cov.liftHomotopy_lifts F f₀' _) (1, a)).trans (F.apply_one a) prop' := rel } @@ -354,44 +356,90 @@ theorem homotopicRel_iff_comp [PreconnectedSpace A] {f₀ f₁ : C(A, E)} {S : S (ContinuousMap.comp ⟨p, cov.continuous⟩ f₀).HomotopicRel (.comp ⟨p, cov.continuous⟩ f₁) S := ⟨fun ⟨F⟩ ↦ ⟨F.compContinuousMap _⟩, fun ⟨F⟩ ↦ ⟨cov.liftHomotopyRel F he rfl rfl⟩⟩ +theorem homotopicRel_liftPath {γ₀ γ₁ : C(I, X)} + (h : γ₀.HomotopicRel γ₁ {0,1}) (e : E) (h₀ : γ₀ 0 = p e) (h₁ : γ₁ 0 = p e) : + (cov.liftPath γ₀ e h₀).HomotopicRel (cov.liftPath γ₁ e h₁) {0,1} := + h.map fun H ↦ cov.liftHomotopyRel (f₀' := cov.liftPath γ₀ e h₀) (f₁' := cov.liftPath γ₁ e h₁) H + ⟨0, .inl rfl, by simp_rw [liftPath_zero]⟩ (liftPath_lifts ..) (liftPath_lifts ..) + /-- Lifting two paths that are homotopic relative to `{0,1}` starting from the same point also ends up in the same point. -/ theorem liftPath_apply_one_eq_of_homotopicRel {γ₀ γ₁ : C(I, X)} (h : γ₀.HomotopicRel γ₁ {0,1}) (e : E) (h₀ : γ₀ 0 = p e) (h₁ : γ₁ 0 = p e) : cov.liftPath γ₀ e h₀ 1 = cov.liftPath γ₁ e h₁ 1 := by - obtain ⟨H⟩ := h - have := cov.liftHomotopyRel (f₀' := cov.liftPath γ₀ e h₀) (f₁' := cov.liftPath γ₁ e h₁) H - ⟨0, .inl rfl, by simp_rw [liftPath_zero]⟩ (liftPath_lifts ..) (liftPath_lifts ..) + have := (cov.homotopicRel_liftPath h e h₀ h₁).some rw [← this.eq_fst 0 (.inr rfl), ← this.eq_snd 0 (.inr rfl)] /-- The monodromy of a covering map `p : E → X`, which sends a lift of the starting point of a path in `X` to the endpoint of the lifted path in `E`. It only depends on the homotopy class of the path. -/ -noncomputable def monodromy {x y : X} (γ : Path.Homotopic.Quotient x y) : +def monodromy {x y : X} (γ : Path.Homotopic.Quotient x y) : p ⁻¹' {x} → p ⁻¹' {y} := fun e ↦ γ.lift (fun γ : Path x y ↦ ⟨cov.liftPath γ e (γ.source.trans e.2.symm) 1, - (congr_fun (cov.liftPath_lifts ..) 1).trans γ.target⟩) + congr($(cov.liftPath_lifts ..) 1).trans γ.target⟩) fun _ _ h ↦ Subtype.ext (cov.liftPath_apply_one_eq_of_homotopicRel h ..) +/-- Lift a homotopy class of paths to a covering space. -/ +def liftPathQuotient {x y : X} (γ : Path.Homotopic.Quotient x y) (e : p ⁻¹' {x}) : + Path.Homotopic.Quotient e.1 (cov.monodromy γ e) := + have he (γ : Path x y) : γ 0 = p (e : E) := by aesop + let g (γ : Path x y) : Path.Homotopic.Quotient (e : E) (cov.liftPath γ (e : E) (he γ) 1) := + .mk ⟨cov.liftPath γ (e : E) (he γ), cov.liftPath_zero .., rfl⟩ + let _i : Setoid (Path x y) := Path.Homotopic.setoid x y + have hg (γ γ' : Path x y) (hγ : γ ≈ γ') : g γ ≍ g γ' := by + refine .trans (heq_of_eq ?_) (Path.Homotopic.Quotient.cast_heq rfl + (cov.liftPath_apply_one_eq_of_homotopicRel hγ _ (he γ) _)) + rw [← Path.Homotopic.Quotient.mk_cast, Path.Homotopic.Quotient.eq] + exact cov.homotopicRel_liftPath hγ _ (by aesop) (by aesop) + γ.hrecOn g hg + +theorem map_liftPathQuotient {x y : X} (γ : Path.Homotopic.Quotient x y) (e : p ⁻¹' {x}) : + (cov.liftPathQuotient γ e).map ⟨p, cov.continuous⟩ = γ.cast e.2 (cov.monodromy γ e).2 := by + obtain ⟨γ⟩ := γ + refine congr_arg Path.Homotopic.Quotient.mk ?_ + ext1 + exact cov.liftPath_lifts _ _ (γ.source.trans e.2.symm) + theorem monodromy_map {x y : E} (γ : Path.Homotopic.Quotient x y) : cov.monodromy (γ.map ⟨p, cov.continuous⟩) ⟨x, rfl⟩ = ⟨y, rfl⟩ := Subtype.ext <| by obtain ⟨γ⟩ := γ - exact (DFunLike.congr_fun ((cov.eq_liftPath_iff' _).mpr ⟨rfl, γ.source⟩).symm 1).trans γ.target + exact congr($((cov.eq_liftPath_iff' _).mpr ⟨rfl, γ.source⟩) 1).symm.trans γ.target + +theorem monodromy_eq_of_map_eq {x y : X} {γ : Path.Homotopic.Quotient x y} + {ex : p ⁻¹' {x}} {ey : p ⁻¹' {y}} (Γ : Path.Homotopic.Quotient ex.1 ey) + (eq : Γ.map ⟨p, cov.continuous⟩ = γ.cast ex.2 ey.2) : + cov.monodromy γ ex = ey := by + convert ← cov.monodromy_map Γ + exacts [ey.2, ex.2, ey.2, by rw [eq]; exact γ.cast_heq .., ey.2] theorem monodromy_refl {x : X} : cov.monodromy (.refl x) = id := - funext fun e ↦ Subtype.ext <| DFunLike.congr_fun (cov.liftPath_const e.2.symm) 1 + funext fun e ↦ Subtype.ext congr($(cov.liftPath_const e.2.symm) 1) theorem monodromy_trans_apply {x y z : X} (γ : Path.Homotopic.Quotient x y) (γ' : Path.Homotopic.Quotient y z) (e) : cov.monodromy (γ.trans γ') e = cov.monodromy γ' (cov.monodromy γ e) := by obtain ⟨γ⟩ := γ; obtain ⟨γ'⟩ := γ' - exact Subtype.ext ((DFunLike.congr_fun (cov.liftPath_trans e.2.symm ..) 1).trans (Path.target _)) + exact Subtype.ext (congr($(cov.liftPath_trans e.2.symm ..) 1).trans (Path.target _)) + +/-- The monodromy action of the fundamental group at `x` on the fiber over `x`. -/ +@[reducible] def fundamentalGroupMulAction (x : X) : + MulAction (FundamentalGroup X x) (p ⁻¹' {x}) := + { smul := cov.monodromy (x := x) (y := x) + mul_smul _ _ _ := cov.monodromy_trans_apply .. + one_smul := congr_fun cov.monodromy_refl } + +/-- The monodromy action of the fundamental group at `x` on the fiber over `x`. -/ +def monodromyPerm (x : X) : FundamentalGroup X x →* Equiv.Perm (p ⁻¹' {x}) := + letI := cov.fundamentalGroupMulAction x + MulAction.toPermHom _ _ + +@[simp] theorem coe_monodromyPerm {x γ} : cov.monodromyPerm x γ = cov.monodromy γ := rfl open CategoryTheory /-- Monodromy of a covering map as a functor. Definition 2.1 in https://ncatlab.org/nlab/show/monodromy. -/ -@[simps] noncomputable def monodromyFunctor : FundamentalGroupoid X ⥤ Type _ where +@[simps] def monodromyFunctor : FundamentalGroupoid X ⥤ Type _ where obj x := p ⁻¹' {x.as} map f := ↾(cov.monodromy f) map_id _ := by ext x : 3; simpa using! congr_fun cov.monodromy_refl x @@ -404,7 +452,7 @@ theorem monodromy_bijective {x y : X} (γ : Path.Homotopic.Quotient x y) : /-- A covering map induces an injection on all Hom-sets of the fundamental groupoid, in particular on the fundamental group. The first part of Proposition 1.31 of [hatcher02]. -/ lemma injective_path_homotopic_map (e₀ e₁ : E) : - Function.Injective fun γ : Path.Homotopic.Quotient e₀ e₁ ↦ γ.map ⟨p, cov.continuous⟩ := by + Injective fun γ : Path.Homotopic.Quotient e₀ e₁ ↦ γ.map ⟨p, cov.continuous⟩ := by refine Quotient.ind₂ fun γ₀ γ₁ ↦ ?_ dsimp only simp only [Path.Homotopic.Quotient.mk''_eq_mk] @@ -418,7 +466,7 @@ alias injective_path_homotopic_mapFn := injective_path_homotopic_map /-- A continuous map `f` from a simply-connected, locally path-connected space `A` to another space `X` lifts uniquely through a covering map `p : E → X`, after specifying any lift `e₀ : E` of any point `a₀ : A`. -/ -theorem existsUnique_continuousMap_lifts [SimplyConnectedSpace A] [LocPathConnectedSpace A] +theorem existsUnique_continuousMap_lifts [SimplyConnectedSpace A] [LocallyPathConnectedSpace A] (f : C(A, X)) (a₀ : A) (e₀ : E) (he : p e₀ = f a₀) : ∃! F : C(A, E), F a₀ = e₀ ∧ p ∘ F = f := by refine cov.isLocalHomeomorph.existsUnique_continuousMap_lifts f a₀ e₀ he (fun γ γ_0 ↦ ?_) @@ -440,7 +488,7 @@ open FundamentalGroup Path.Homotopic.Quotient in if `f⁎ π₁(A, a₀) ⊆ p⁎ π₁(E, e₀)`. Proposition 1.33 of [hatcher02], known as the lifting criterion. -/ theorem existsUnique_continuousMap_lifts_of_range_le - [PathConnectedSpace A] [LocPathConnectedSpace A] + [PathConnectedSpace A] [LocallyPathConnectedSpace A] {f : C(A, X)} {a₀ : A} {e₀ : E} (he : p e₀ = f a₀) (le : (map f a₀).range ≤ (mapOfEq ⟨p, cov.continuous⟩ he).range) : ∃! F : C(A, E), F a₀ = e₀ ∧ p ∘ F = f := by @@ -459,10 +507,9 @@ theorem existsUnique_continuousMap_lifts_of_range_le conv_rhs => rw [← eq.2 ⟨.reflTransSymm _⟩, mk_refl, monodromy_refl] rw [Path.map_symm, ← Path.map_trans] set pγγ' : Path a₀ a₀ := pγ.trans pγ'.symm - have ⟨pΓΓ', eq⟩ := le ⟨fromPath (.mk pγγ'), rfl⟩ - obtain ⟨pΓΓ', rfl⟩ := mk_surjective pΓΓ' + obtain ⟨⟨pΓΓ'⟩, eq⟩ := le ⟨fromPath (.mk pγγ'), rfl⟩ rw [mapOfEq_apply, map_apply, ← mk_map] at eq - exact eq ▸ Subtype.ext (congr_arg (·.1) (cov.monodromy_map (.mk pΓΓ'))) + exact eq ▸ Subtype.ext congr($(cov.monodromy_map <| .mk _)) end homotopy_lifting @@ -478,7 +525,7 @@ Given a point `a₀` in the domain of `f` and a lift `e₀` of `f a₀` along `p there exists a unique lift `F` of `f` along `p` such that `F a₀ = e₀`. -/ theorem IsCoveringMapOn.existsUnique_continuousMap_lifts [SimplyConnectedSpace A] - [LocPathConnectedSpace A] {s : Set X} (cov : IsCoveringMapOn p s) (f : C(A, X)) {a₀ : A} + [LocallyPathConnectedSpace A] {s : Set X} (cov : IsCoveringMapOn p s) (f : C(A, X)) {a₀ : A} {e₀ : E} (he : p e₀ = f a₀) (hs : ∀ a, f a ∈ s) : ∃! F : C(A, E), F a₀ = e₀ ∧ p ∘ F = f := by obtain ⟨f, rfl⟩ : ∃ f' : C(A, s), f = .comp ⟨Subtype.val, by fun_prop⟩ f' := @@ -496,3 +543,221 @@ theorem IsCoveringMapOn.existsUnique_continuousMap_lifts [SimplyConnectedSpace A ⟨Subtype.ext hF'₁, ?_⟩ · ext; simp [← hF'₂] · ext; simp [← hF_unique] + +namespace IsQuotientCoveringMap + +variable {G : Type*} [Group G] [MulAction G E] (hp : IsQuotientCoveringMap p G) {g : G} + +/-- The monodromy action of a quotient covering map commutes with the group action. -/ +theorem monodromy_toPermFiber {x y : X} {γ : Path.Homotopic.Quotient x y} {e : p ⁻¹' {x}} : + letI monodromy := hp.isCoveringMap.monodromy + monodromy γ (hp.toPermFiber x g e) = hp.toPermFiber y g (monodromy γ e) := + let Γ := hp.isCoveringMap.liftPathQuotient γ e + let g' : C(E, E) := ⟨_, hp.toContinuousConstSMul.continuous_const_smul g⟩ + let p' : C(E, X) := ⟨p, hp.continuous⟩ + have hgp : p'.comp g' = p' := by ext; simp [g', p', hp.map_smul] + hp.isCoveringMap.monodromy_eq_of_map_eq (Γ.map g') <| show (Γ.map g').map p' = _ by + rw [← Path.Homotopic.Quotient.map_comp] + convert hp.isCoveringMap.map_liftPathQuotient γ e using 2 + · simp [g', p', hp.map_smul] + · simp [g', p', hp.map_smul] + · grind + · grind + +theorem commute_monodromyPerm_toPermFiber {x : X} {γ : FundamentalGroup X x} : + Commute (hp.isCoveringMap.monodromyPerm x γ) (hp.toPermFiber x g) := by + ext; exact congr($hp.monodromy_toPermFiber) + +theorem monodromy_ext_iff {x y : X} {γ γ' : Path.Homotopic.Quotient x y} (e : p ⁻¹' {x}) : + letI monodromy := hp.isCoveringMap.monodromy + monodromy γ e = monodromy γ' e ↔ monodromy γ = monodromy γ' where + mp eq := by + ext e' + obtain ⟨g, rfl⟩ := hp.exists_toPermFiber_eq e e' + simp_rw [monodromy_toPermFiber, eq] + mpr := (congr_fun · _) + +alias ⟨monodromy_ext, _⟩ := monodromy_ext_iff + +variable {x : X} (e : p ⁻¹' {x}) {γ : FundamentalGroup X x} + +theorem monodromy_eq_id_iff : + hp.isCoveringMap.monodromy γ = id ↔ hp.isCoveringMap.monodromy γ e = e where + mp := (congr_fun · _) + mpr eq := (hp.monodromy_ext e (eq.trans congr($hp.isCoveringMap.monodromy_refl e).symm)).trans + hp.isCoveringMap.monodromy_refl + +theorem ker_monodromyPerm : + (hp.isCoveringMap.monodromyPerm x).ker = + (FundamentalGroup.mapOfEq ⟨p, hp.continuous⟩ e.2).range := by + ext γ; constructor <;> intro h + · refine ⟨(hp.isCoveringMap.liftPathQuotient γ e).cast rfl congr($h.symm e), ?_⟩ + rw [FundamentalGroup.mapOfEq_apply, + Path.Homotopic.Quotient.map_cast, IsCoveringMap.map_liftPathQuotient] + aesop + · obtain ⟨γ, rfl⟩ := h + refine DFunLike.ext' <| + (hp.monodromy_eq_id_iff e).mpr <| hp.isCoveringMap.monodromy_eq_of_map_eq γ ?_ + aesop (add simp FundamentalGroup.mapOfEq_apply) + +theorem monodromyPerm_injective [SimplyConnectedSpace E] : + Injective (hp.isCoveringMap.monodromyPerm x) := by + let e : p⁻¹' {x} := ⟨(hp.surjective x).choose, (hp.surjective x).choose_spec⟩ + rw [← MonoidHom.ker_eq_bot_iff, hp.ker_monodromyPerm e] + set f : FundamentalGroup E (e : E) →* FundamentalGroup X x := + FundamentalGroup.mapOfEq ⟨p, hp.continuous⟩ e.2 + have : Subsingleton f.range := (Set.subsingleton_coe _).mpr f.subsingleton_coe_range + exact Subgroup.eq_bot_of_subsingleton _ + +open MulOpposite in +/-- Choosing an arbitrary basepoint `e ∈ f ⁻¹' {x}` induces a bijection `f ⁻¹' {x} ≃ G`, and the +`G`-action on `f ⁻¹' {x}` corresponds to left multiplication. The monodromy action commutes +with the `G`-action, so each monodromy must corresponds must correspond to a right multiplication. +-/ +def fundamentalGroupToMulOpposite : FundamentalGroup X x →* Gᵐᵒᵖ where + toFun γ := op <| hp.fiberEquivGroup e (hp.isCoveringMap.monodromy γ e) + map_one' := by rw [FundamentalGroup.one_def, IsCoveringMap.monodromy_refl]; simp + map_mul' γ γ' := by + rw [FundamentalGroup.mul_def, IsCoveringMap.monodromy_trans_apply, ← op_mul, op_inj] + apply hp.isCancelSMul.right_cancel _ _ e.1 + simp_rw [mul_smul, fiberEquivGroup_smul_self, ← hp.toPermFiber_apply_apply_coe] + congr + refine .trans ?_ hp.monodromy_toPermFiber + congr + exact Subtype.ext (fiberEquivGroup_smul_self ..).symm + +variable {e} in +theorem fundamentalGroupToMulOpposite_apply_eq_Iff {g : Gᵐᵒᵖ} : + hp.fundamentalGroupToMulOpposite e γ = g ↔ g.unop • e.1 = hp.isCoveringMap.monodromy γ e := by + rw [fundamentalGroupToMulOpposite, ← MulOpposite.unop_injective.eq_iff, iff_comm, eq_comm, + ← hp.fiberEquivGroup_smul_self e] + have := hp.isCancelSMul.right_cancel' + aesop + +variable {e} in +theorem unop_fundamentalGroupToMulOpposite_smul : + (hp.fundamentalGroupToMulOpposite e γ).unop • e.1 = hp.isCoveringMap.monodromy γ e := by + simp [fundamentalGroupToMulOpposite, fiberEquivGroup_smul_self] + +variable {e} in +theorem fundamentalGroupToMulOpposite_eq_one_iff : + hp.fundamentalGroupToMulOpposite e γ = 1 ↔ hp.isCoveringMap.monodromy γ e = e where + mp h := Subtype.ext <| by rw [← hp.unop_fundamentalGroupToMulOpposite_smul, h]; apply one_smul + mpr h := MulOpposite.unop_injective <| hp.isCancelSMul.right_cancel _ _ e.1 <| by + simp [fundamentalGroupToMulOpposite, h] + +theorem ker_fundamentalGroupToMulOpposite : + (hp.fundamentalGroupToMulOpposite e).ker = (hp.isCoveringMap.monodromyPerm x).ker := by + ext; simp [fundamentalGroupToMulOpposite_eq_one_iff, DFunLike.ext'_iff, ← hp.monodromy_eq_id_iff] + +theorem fundamentalGroupToMulOpposite_surjective [PathConnectedSpace E] : + Surjective (hp.fundamentalGroupToMulOpposite e) := by + intro g + set e' : p⁻¹' {x} := ⟨MulOpposite.unop g • (e : E), by + have := hp.map_smul (e := e) (MulOpposite.unop g); aesop⟩ with he' + set Γ : Path (e : E) (e' : E) := + { toFun := PathConnectedSpace.somePath (e : E) (e' : E) + continuous_toFun := by fun_prop + source' := by simp + target' := by simp } + set γ : Path x x := (Γ.map hp.continuous).cast + (by simpa using e.property.symm) (by simpa using e'.property.symm) + use .fromPath ⟦γ⟧ + rw [fundamentalGroupToMulOpposite_apply_eq_Iff] + change (e' : E) = _ + rw [← hp.isCoveringMap.monodromy_eq_of_map_eq (γ := ⟦γ⟧) (Γ := ⟦Γ⟧) rfl] + +lemma fundamentalGroupToMulOpposite_injective [SimplyConnectedSpace E] : + Injective (hp.fundamentalGroupToMulOpposite e) := by + rw [← MonoidHom.ker_eq_bot_iff, ker_fundamentalGroupToMulOpposite, MonoidHom.ker_eq_bot_iff] + exact hp.monodromyPerm_injective + +/-- The fundamental group of the base of simply-connected covering map is contravariantly +equivalent to the group of the covering map. -/ +def fundamentalGroupEquiv [SimplyConnectedSpace E] : + FundamentalGroup X x ≃* Gᵐᵒᵖ := + MulEquiv.ofBijective (hp.fundamentalGroupToMulOpposite e) + ⟨hp.fundamentalGroupToMulOpposite_injective e, + hp.fundamentalGroupToMulOpposite_surjective e⟩ + +end IsQuotientCoveringMap + +namespace IsAddQuotientCoveringMap + +variable {G : Type*} [AddGroup G] [AddAction G E] (hp : IsAddQuotientCoveringMap p G) {g : G} + +theorem monodromy_toPermFiber {x y : X} {γ : Path.Homotopic.Quotient x y} {e : p ⁻¹' {x}} : + letI monodromy := hp.isCoveringMap.monodromy + monodromy γ (hp.toMultiplicative.toPermFiber x g e) = + hp.toMultiplicative.toPermFiber y g (monodromy γ e) := + hp.toMultiplicative.monodromy_toPermFiber + +theorem commute_monodromyPerm_toPermFiber {x : X} {γ : FundamentalGroup X x} : + Commute + (hp.isCoveringMap.monodromyPerm x γ) + (hp.toMultiplicative.toPermFiber x g) := + hp.toMultiplicative.commute_monodromyPerm_toPermFiber + +theorem monodromy_ext_iff {x y : X} {γ γ' : Path.Homotopic.Quotient x y} (e : p ⁻¹' {x}) : + letI monodromy := hp.isCoveringMap.monodromy + monodromy γ e = monodromy γ' e ↔ monodromy γ = monodromy γ' := + hp.toMultiplicative.monodromy_ext_iff e + +alias ⟨monodromy_ext, _⟩ := monodromy_ext_iff + +variable {x : X} (e : p ⁻¹' {x}) {γ : FundamentalGroup X x} + +theorem monodromy_eq_id_iff : + hp.isCoveringMap.monodromy γ = id ↔ hp.isCoveringMap.monodromy γ e = e := + hp.toMultiplicative.monodromy_eq_id_iff e + +theorem ker_monodromyPerm : + (hp.isCoveringMap.monodromyPerm x).ker = + (FundamentalGroup.mapOfEq ⟨p, hp.continuous⟩ e.2).range := + hp.toMultiplicative.ker_monodromyPerm e + +theorem monodromyPerm_injective [SimplyConnectedSpace E] : + Injective (hp.isCoveringMap.monodromyPerm x) := + hp.toMultiplicative.monodromyPerm_injective + +/-- Choosing an arbitrary basepoint `e ∈ f ⁻¹' {x}` induces a bijection `f ⁻¹' {x} ≃ G`, and the +`G`-action on `f ⁻¹' {x}` corresponds to left multiplication. The monodromy action commutes +with the `G`-action, so each monodromy must corresponds must correspond to a right multiplication. +-/ +def fundamentalGroupToMulOpposite : FundamentalGroup X x →* (Multiplicative G)ᵐᵒᵖ := + hp.toMultiplicative.fundamentalGroupToMulOpposite e + +variable {e} in +theorem fundamentalGroupToMulOpposite_apply_eq_Iff {g : (Multiplicative G)ᵐᵒᵖ} : + hp.fundamentalGroupToMulOpposite e γ = g ↔ g.unop • e.1 = hp.isCoveringMap.monodromy γ e := + hp.toMultiplicative.fundamentalGroupToMulOpposite_apply_eq_Iff + +variable {e} in +theorem unop_fundamentalGroupToMulOpposite_smul : + (hp.fundamentalGroupToMulOpposite e γ).unop • e.1 = hp.isCoveringMap.monodromy γ e := + hp.toMultiplicative.unop_fundamentalGroupToMulOpposite_smul + +variable {e} in +theorem fundamentalGroupToMulOpposite_eq_one_iff : + hp.fundamentalGroupToMulOpposite e γ = 1 ↔ hp.isCoveringMap.monodromy γ e = e := + hp.toMultiplicative.fundamentalGroupToMulOpposite_eq_one_iff + +theorem ker_fundamentalGroupToMulOpposite : + (hp.fundamentalGroupToMulOpposite e).ker = (hp.isCoveringMap.monodromyPerm x).ker := + hp.toMultiplicative.ker_fundamentalGroupToMulOpposite e + +theorem fundamentalGroupToMulOpposite_surjective [PathConnectedSpace E] : + Surjective (hp.fundamentalGroupToMulOpposite e) := + hp.toMultiplicative.fundamentalGroupToMulOpposite_surjective e + +lemma fundamentalGroupToMulOpposite_injective [SimplyConnectedSpace E] : + Injective (hp.fundamentalGroupToMulOpposite e) := + hp.toMultiplicative.fundamentalGroupToMulOpposite_injective e + +/-- The fundamental group of the base of simply-connected covering map is contravariantly +equivalent to the group of the covering map. -/ +def fundamentalGroupEquiv [SimplyConnectedSpace E] : + FundamentalGroup X x ≃* (Multiplicative G)ᵐᵒᵖ := + hp.toMultiplicative.fundamentalGroupEquiv e + +end IsAddQuotientCoveringMap diff --git a/Mathlib/Topology/Homotopy/LocallyContractible.lean b/Mathlib/Topology/Homotopy/LocallyContractible.lean index 746ef1a8a3ea24..1f2349bda7e995 100644 --- a/Mathlib/Topology/Homotopy/LocallyContractible.lean +++ b/Mathlib/Topology/Homotopy/LocallyContractible.lean @@ -7,7 +7,7 @@ module public import Mathlib.Topology.Homotopy.Contractible public import Mathlib.Topology.Homotopy.Basic -public import Mathlib.Topology.Connected.LocPathConnected +public import Mathlib.Topology.Connected.LocallyPathConnected public import Mathlib.Topology.Homeomorph.Lemmas /-! @@ -24,7 +24,7 @@ This file defines `LocallyContractibleSpace` and `StronglyLocallyContractibleSpa ## Main results * `StronglyLocallyContractibleSpace.locallyContractible`: SLC implies classical LC -* `instLocPathConnectedSpace`: strongly locally contractible spaces are locally path-connected +* `instLocallyPathConnectedSpace`: strongly locally contractible spaces are locally path-connected * `StronglyLocallyContractibleSpace.of_bases`: a helper to construct strongly locally contractible spaces from a neighborhood basis * `contractible_subset_basis`: basis of contractible neighborhoods contained in an open set @@ -122,7 +122,7 @@ theorem contractible_subset_basis {U : Set X} (h : IsOpen U) (hx : x ∈ U) : (contractible_basis x).hasBasis_self_subset (IsOpen.mem_nhds h hx) /-- Strongly locally contractible spaces are locally path-connected. -/ -instance (priority := 100) instLocPathConnectedSpace : LocPathConnectedSpace X where +instance (priority := 100) instLocallyPathConnectedSpace : LocallyPathConnectedSpace X where path_connected_basis x := by refine contractible_basis x |>.to_hasBasis' (fun s ⟨hs, hs'⟩ ↦ ⟨s, ⟨hs, ?_⟩, le_rfl⟩) (fun s hs ↦ hs.1) diff --git a/Mathlib/Topology/IndicatorConstPointwise.lean b/Mathlib/Topology/IndicatorConstPointwise.lean index df11b94be10a5e..cb3c518d5e5009 100644 --- a/Mathlib/Topology/IndicatorConstPointwise.lean +++ b/Mathlib/Topology/IndicatorConstPointwise.lean @@ -19,11 +19,11 @@ functions of sets converge pointwise. For `A` a set, `(Asᵢ)` an indexed collection of sets, under mild conditions, the following are equivalent: - (a) the indicator functions of `Asᵢ` tend to the indicator function of `A` pointwise; +(a) the indicator functions of `Asᵢ` tend to the indicator function of `A` pointwise; - (b) for every `x`, we eventually have that `x ∈ Asᵢ` holds iff `x ∈ A` holds; +(b) for every `x`, we eventually have that `x ∈ Asᵢ` holds iff `x ∈ A` holds; - (c) `Tendsto As _ <| Filter.pi (pure <| · ∈ A)`. +(c) `Tendsto As _ <| Filter.pi (pure <| · ∈ A)`. The results stating these in the case when the indicators take values in a Fréchet space are: * `tendsto_indicator_const_iff_forall_eventually` is the equivalence (a) ↔ (b); diff --git a/Mathlib/Topology/Inseparable.lean b/Mathlib/Topology/Inseparable.lean index cd48ad7ae8dc44..548e63f3a83ded 100644 --- a/Mathlib/Topology/Inseparable.lean +++ b/Mathlib/Topology/Inseparable.lean @@ -413,7 +413,7 @@ lemma Topology.IsInducing.generalizingMap (hf : IsInducing f) obtain ⟨y, rfl⟩ := h e ⟨x, rfl⟩ exact ⟨_, hf.specializes_iff.mp e, rfl⟩ -lemma IsOpenEmbedding.generalizingMap (hf : IsOpenEmbedding f) : GeneralizingMap f := +lemma Topology.IsOpenEmbedding.generalizingMap (hf : IsOpenEmbedding f) : GeneralizingMap f := hf.isInducing.generalizingMap hf.isOpen_range.stableUnderGeneralization lemma SpecializingMap.stableUnderSpecialization_range (h : SpecializingMap f) : diff --git a/Mathlib/Topology/Instances/ENNReal/Lemmas.lean b/Mathlib/Topology/Instances/ENNReal/Lemmas.lean index 1e3ffeaae4adad..06a8d14917dfe5 100644 --- a/Mathlib/Topology/Instances/ENNReal/Lemmas.lean +++ b/Mathlib/Topology/Instances/ENNReal/Lemmas.lean @@ -464,7 +464,7 @@ theorem inv_liminf {ι : Sort _} {x : ι → ℝ≥0∞} {l : Filter ι} : @[fun_prop] protected theorem continuous_zpow : ∀ n : ℤ, Continuous (· ^ n : ℝ≥0∞ → ℝ≥0∞) | (n : ℕ) => mod_cast ENNReal.continuous_pow n - | .negSucc n => by simpa using (ENNReal.continuous_pow _).inv + | .negSucc n => by simpa using (ENNReal.continuous_pow _).fun_inv @[deprecated (since := "2026-01-15")] protected alias tendsto_inv_iff := tendsto_inv_iff diff --git a/Mathlib/Topology/Irreducible.lean b/Mathlib/Topology/Irreducible.lean index 8f5fb12cf5bd77..e7e91d08c1cfee 100644 --- a/Mathlib/Topology/Irreducible.lean +++ b/Mathlib/Topology/Irreducible.lean @@ -524,8 +524,8 @@ end lemma IsDiscrete.subsingleton_of_isPreirreducible (hs : IsDiscrete s) (hs' : IsPreirreducible s) : s.Subsingleton := by intro x hxs y hys - obtain ⟨U, hU, hUx⟩ := isDiscrete_iff_forall_exists_isOpen.mp hs x hxs - obtain ⟨V, hV, hVy⟩ := isDiscrete_iff_forall_exists_isOpen.mp hs y hys + obtain ⟨U, hU, hUx⟩ := isDiscrete_iff_forall_mem_exists_isOpen.mp hs x hxs + obtain ⟨V, hV, hVy⟩ := isDiscrete_iff_forall_mem_exists_isOpen.mp hs y hys obtain ⟨z, hz⟩ := hs' _ _ hU hV ⟨x, by grind⟩ ⟨y, by grind⟩ exact (hUx.le (by grind)).symm.trans (b := z) (hVy.le (by grind)) diff --git a/Mathlib/Topology/KrullDimension.lean b/Mathlib/Topology/KrullDimension.lean index 187f1e25b66e46..0b55b422398669 100644 --- a/Mathlib/Topology/KrullDimension.lean +++ b/Mathlib/Topology/KrullDimension.lean @@ -45,7 +45,7 @@ variable {X Y : Type*} [TopologicalSpace X] [TopologicalSpace Y] ### Main dimension theorems -/ /-- If `f : Y → X` is inducing, then `dim(Y) ≤ dim(X)`. -/ -theorem IsInducing.topologicalKrullDim_le {f : Y → X} (hf : IsInducing f) : +theorem Topology.IsInducing.topologicalKrullDim_le {f : Y → X} (hf : IsInducing f) : topologicalKrullDim Y ≤ topologicalKrullDim X := krullDim_le_of_strictMono _ (map_strictMono_of_isInducing hf) @@ -53,16 +53,16 @@ theorem IsInducing.topologicalKrullDim_le {f : Y → X} (hf : IsInducing f) : theorem IsHomeomorph.topologicalKrullDim_eq (f : X → Y) (h : IsHomeomorph f) : topologicalKrullDim X = topologicalKrullDim Y := have fwd : topologicalKrullDim X ≤ topologicalKrullDim Y := - IsInducing.topologicalKrullDim_le h.isClosedEmbedding.toIsInducing + h.isInducing.topologicalKrullDim_le have bwd : topologicalKrullDim Y ≤ topologicalKrullDim X := - IsInducing.topologicalKrullDim_le (h.homeomorph f).symm.isClosedEmbedding.toIsInducing + (h.homeomorph f).symm.isInducing.topologicalKrullDim_le le_antisymm fwd bwd /-- The topological Krull dimension of any subspace is at most the dimension of the ambient space. -/ theorem topologicalKrullDim_subspace_le (X : Type*) [TopologicalSpace X] (Y : Set X) : topologicalKrullDim Y ≤ topologicalKrullDim X := - IsInducing.topologicalKrullDim_le IsInducing.subtypeVal + IsInducing.subtypeVal.topologicalKrullDim_le theorem topologicalKrullDim_zero_of_discreteTopology (X : Type*) [TopologicalSpace X] [DiscreteTopology X] : diff --git a/Mathlib/Topology/LocalAtTarget.lean b/Mathlib/Topology/LocalAtTarget.lean index 6bb1eb268b78e7..1ff14ed4d98080 100644 --- a/Mathlib/Topology/LocalAtTarget.lean +++ b/Mathlib/Topology/LocalAtTarget.lean @@ -224,10 +224,7 @@ lemma isOpenMap_iff_comp : IsOpenMap f ↔ ∀ i, IsOpenMap (f ∘ ((↑) : U i lemma generalizingMap_iff_comp : GeneralizingMap f ↔ ∀ i, GeneralizingMap (f ∘ ((↑) : U i → α)) := by - refine ⟨fun hf ↦ fun i ↦ - ((U i).isOpenEmbedding'.generalizingMap - (U i).isOpenEmbedding'.isOpen_range.stableUnderGeneralization).comp hf, - fun hf ↦ fun x y h ↦ ?_⟩ + refine ⟨fun hf i ↦ ((U i).isOpenEmbedding'.generalizingMap).comp hf, fun hf ↦ fun x y h ↦ ?_⟩ obtain ⟨i, hi⟩ := hU.exists_mem x replace h : y ⤳ (f ∘ ((↑) : U i → α)) ⟨x, hi⟩ := h obtain ⟨a, ha, rfl⟩ := hf i h diff --git a/Mathlib/Topology/Maps/Basic.lean b/Mathlib/Topology/Maps/Basic.lean index c5f7c9ca50bb24..4efc1e7e0b3e4e 100644 --- a/Mathlib/Topology/Maps/Basic.lean +++ b/Mathlib/Topology/Maps/Basic.lean @@ -369,6 +369,12 @@ protected theorem id : IsOpenMap (@id X) := fun s hs => by rwa [image_id] protected theorem comp (hg : IsOpenMap g) (hf : IsOpenMap f) : IsOpenMap (g ∘ f) := fun s hs => by rw [image_comp]; exact hg _ (hf _ hs) +/-- If `g ∘ f` is open, where `f` is continuous and surjective, then `g` is open. -/ +theorem of_comp (hf : Continuous f) (f_surj : Surjective f) (h : IsOpenMap (g ∘ f)) : + IsOpenMap g := fun s hs => by + rw [← f_surj.image_preimage s, ← image_comp] + exact h _ (hs.preimage hf) + theorem isOpen_range (hf : IsOpenMap f) : IsOpen (range f) := by rw [← image_univ] exact hf _ isOpen_univ diff --git a/Mathlib/Topology/Maps/OpenQuotient.lean b/Mathlib/Topology/Maps/OpenQuotient.lean index be2ce3cb93e7f3..139d7c8ec14f05 100644 --- a/Mathlib/Topology/Maps/OpenQuotient.lean +++ b/Mathlib/Topology/Maps/OpenQuotient.lean @@ -49,6 +49,15 @@ theorem comp {g : Y → Z} (hg : IsOpenQuotientMap g) (hf : IsOpenQuotientMap f) IsOpenQuotientMap (g ∘ f) := ⟨.comp hg.1 hf.1, .comp hg.2 hf.2, .comp hg.3 hf.3⟩ +theorem of_comp {g : Y → Z} (hf : Continuous f) (f_surj : Surjective f) (hg : Continuous g) + (h : IsOpenQuotientMap (g ∘ f)) : IsOpenQuotientMap g := + ⟨.of_comp h.surjective, hg, .of_comp hf f_surj h.isOpenMap ⟩ + +theorem of_comp_iff {g : Y → Z} (hf : IsOpenQuotientMap f) : + IsOpenQuotientMap (g ∘ f) ↔ IsOpenQuotientMap g := + ⟨fun h ↦ .of_comp hf.continuous hf.surjective + (hf.isQuotientMap.continuous_iff.mpr h.continuous) h, fun hg ↦ hg.comp hf⟩ + theorem map_nhds_eq (h : IsOpenQuotientMap f) (x : X) : map f (𝓝 x) = 𝓝 (f x) := le_antisymm h.continuous.continuousAt <| h.isOpenMap.nhds_le _ @@ -60,6 +69,10 @@ theorem continuousAt_comp_iff (h : IsOpenQuotientMap f) {g : Y → Z} {x : X} : ContinuousAt (g ∘ f) x ↔ ContinuousAt g (f x) := by simp only [ContinuousAt, ← h.map_nhds_eq, tendsto_map'_iff, comp_def] +theorem isOpenMap_iff (hf : IsOpenQuotientMap f) {g : Y → Z} : + IsOpenMap g ↔ IsOpenMap (g ∘ f) := + ⟨fun hg ↦ hg.comp hf.isOpenMap, fun h ↦ .of_comp hf.continuous hf.surjective h⟩ + theorem dense_preimage_iff (h : IsOpenQuotientMap f) {s : Set Y} : Dense (f ⁻¹' s) ↔ Dense s := ⟨fun hs ↦ h.surjective.denseRange.dense_of_mapsTo h.continuous hs (mapsTo_preimage _ _), fun hs ↦ hs.preimage h.isOpenMap⟩ diff --git a/Mathlib/Topology/Maps/Strict/Basic.lean b/Mathlib/Topology/Maps/Strict/Basic.lean index dedc38c5e810f8..df5a1e96472a03 100644 --- a/Mathlib/Topology/Maps/Strict/Basic.lean +++ b/Mathlib/Topology/Maps/Strict/Basic.lean @@ -9,6 +9,8 @@ public import Mathlib.Topology.Maps.Basic public import Mathlib.Topology.Homeomorph.Quotient public import Mathlib.Topology.Constructions public import Mathlib.Data.Setoid.Basic +public import Mathlib.Topology.Algebra.Group.Quotient + /-! # Bourbaki Strict Maps @@ -33,6 +35,18 @@ We provide several equivalent ways to characterize a strict map `f`: the canonical bijection `Quotient (Setoid.ker f) ≃ Set.range f` is a homeomorphism. * `Topology.isStrictMap_iff_isEmbedding_kerLift`: `f` is strict if and only if the canonical injection `Quotient (Setoid.ker f) → Y` (`Setoid.kerLift f`) is an embedding. + +### Group homomorphisms + +In general, the product (in the sense of `Prod.map`) of two strict maps need not be strict. +But thanks to `MonoidHom.isOpenQuotientMap_of_isQuotientMap`, we can replace `IsQuotientMap` +by `IsOpenQuotientMap` in the setting of group homomorphisms. Therefore we provide several +important properties of strict group homomorphisms : + +* `isStrictMap_iff_isOpenQuotientMap_rangeRestrict`: `f` is a strict group homomorphism if + and only if the `rangeRestrict` of `f` is an open quotient map. +* `isStrictMap_prodMap`: The product (in the sense of Prod.map) of strict group homomorphisms + is strict. -/ @[expose] public section @@ -153,4 +167,52 @@ lemma isEmbedding_iff_isStrictMap_injective : (Homeomorph.Quotient.congrRight <| by simp [f_inj.eq_iff]).trans Homeomorph.quotientBot exact f_strict.comp Φ.symm.isEmbedding +/-- Homeomorphisms are precisely bijective strict maps. -/ +lemma isHomeomorph_iff_isStrictMap_bijective : + IsHomeomorph f ↔ IsStrictMap f ∧ Bijective f := by + simp [isHomeomorph_iff_isEmbedding_surjective, isEmbedding_iff_isStrictMap_injective, Bijective, + and_assoc] + +/-- Strict maps are preserved when precomposing with a homeomorphism. -/ +lemma Homeomorph.isStrictMap_comp_iff (e : X ≃ₜ Y) {f : Y → Z} : + IsStrictMap (f ∘ e) ↔ IsStrictMap f := + e.isQuotientMap.isStrictMap_iff.symm + +/-- Strict maps are preserved when postcomposing with a homeomorphism. -/ +lemma Homeomorph.comp_isStrictMap_iff (e : Y ≃ₜ Z) {f : X → Y} : + IsStrictMap (e ∘ f) ↔ IsStrictMap f := + e.isEmbedding.isStrictMap_iff.symm + end Topology + +namespace MonoidHom + +variable {G H G' H' : Type*} [Group G'] [Group H'] [Group G] [Group H] (f : G →* H) (g : G' →* H') + [TopologicalSpace G] [IsTopologicalGroup G] [TopologicalSpace H] + +/-- A group homomorphism is strict if and only if its `rangeRestrict` is an open quotient map. -/ +@[to_additive] lemma isStrictMap_iff_isOpenQuotientMap_rangeRestrict : + IsStrictMap f ↔ IsOpenQuotientMap f.rangeRestrict := by + rw [isOpenQuotientMap_iff_isQuotientMap] + rfl + +variable {f g} [TopologicalSpace G'] [IsTopologicalGroup G'] [TopologicalSpace H'] + +/-- The product (in the sense of `Prod.map`) of group homomorphisms is strict if and only if each +of the morphisms is strict. -/ +@[to_additive isStrictMap_prodMap_iff] lemma isStrictMap_prodMap_iff : + IsStrictMap (f.prodMap g) ↔ IsStrictMap f ∧ IsStrictMap g := by + simp_rw [isStrictMap_iff_isOpenQuotientMap_rangeRestrict] + let Φ : (f.prodMap g).range ≃ₜ f.range × g.range := + (Homeomorph.setCongr (by simp [Subgroup.coe_prod])).trans (Homeomorph.Set.prod _ _) + have eq : Φ ∘ (f.prodMap g).rangeRestrict = f.rangeRestrict.prodMap g.rangeRestrict := rfl + rw [← Φ.comp_isOpenQuotientMap_iff, eq, MonoidHom.coe_prodMap, isOpenQuotientMap_prodMap_iff] + +/-- The product (in the sense of `Prod.map`) of strict group homomorphisms is strict -/ +@[to_additive isStrictMap_prodMap] lemma isStrictMap_prodMap (hf : IsStrictMap f) + (hg : IsStrictMap g) : IsStrictMap (f.prodMap g) := + isStrictMap_prodMap_iff.mpr ⟨hf, hg⟩ + +-- TODO Add the lemma `isStrictMap_piMap` once `MonoidHom.piMap` has been defined. + +end MonoidHom diff --git a/Mathlib/Topology/MetricSpace/Bounded.lean b/Mathlib/Topology/MetricSpace/Bounded.lean index 50d0a217cdaf41..bd4e06e4f1504f 100644 --- a/Mathlib/Topology/MetricSpace/Bounded.lean +++ b/Mathlib/Topology/MetricSpace/Bounded.lean @@ -589,10 +589,10 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: the diameter of a set is always nonnegative. -/ @[positivity Metric.diam _] -meta def evalDiam : PositivityExt where eval {u α} _zα pα? e := do +meta def evalDiam : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@Metric.diam _ $inst $s) => - let some _ := pα? | pure .none assertInstancesCommute pure (.nonnegative q(Metric.diam_nonneg)) | _, _, _ => throwError "not ‖ · ‖" diff --git a/Mathlib/Topology/MetricSpace/Closeds.lean b/Mathlib/Topology/MetricSpace/Closeds.lean index 4590e00f483bd8..84b99c80909c4a 100644 --- a/Mathlib/Topology/MetricSpace/Closeds.lean +++ b/Mathlib/Topology/MetricSpace/Closeds.lean @@ -277,82 +277,6 @@ theorem isClosed_in_closeds [CompleteSpace α] : IsClosed (range <| @NonemptyCompacts.toCloseds α _ _) := NonemptyCompacts.isClosedEmbedding_toCloseds.isClosed_range -/-- In a second countable space, the type of nonempty compact subsets is second countable -/ -instance instSecondCountableTopology [SecondCountableTopology α] : - SecondCountableTopology (NonemptyCompacts α) := - haveI : SeparableSpace (NonemptyCompacts α) := by - /- To obtain a countable dense subset of `NonemptyCompacts α`, start from - a countable dense subset `s` of α, and then consider all its finite nonempty subsets. - This set is countable and made of nonempty compact sets. It turns out to be dense: - by total boundedness, any compact set `t` can be covered by finitely many small balls, and - approximations in `s` of the centers of these balls give the required finite approximation - of `t`. -/ - rcases exists_countable_dense α with ⟨s, cs, s_dense⟩ - let v0 := { t : Set α | t.Finite ∧ t ⊆ s } - let v : Set (NonemptyCompacts α) := { t : NonemptyCompacts α | (t : Set α) ∈ v0 } - refine ⟨⟨v, ?_, ?_⟩⟩ - · have : v0.Countable := countable_setOf_finite_subset cs - exact this.preimage SetLike.coe_injective - · refine fun t => EMetric.mem_closure_iff.2 fun ε εpos => ?_ - -- t is a compact nonempty set, that we have to approximate uniformly by a a set in `v`. - rcases exists_between εpos with ⟨δ, δpos, δlt⟩ - have δpos' : 0 < δ / 2 := ENNReal.half_pos δpos.ne' - -- construct a map F associating to a point in α an approximating point in s, up to δ/2. - have Exy : ∀ x, ∃ y, y ∈ s ∧ edist x y < δ / 2 := by - intro x - rcases EMetric.mem_closure_iff.1 (s_dense x) (δ / 2) δpos' with ⟨y, ys, hy⟩ - exact ⟨y, ⟨ys, hy⟩⟩ - let F x := (Exy x).choose - have Fspec : ∀ x, F x ∈ s ∧ edist x (F x) < δ / 2 := fun x => (Exy x).choose_spec - -- cover `t` with finitely many balls. Their centers form a set `a` - have : TotallyBounded (t : Set α) := t.isCompact.totallyBounded - obtain ⟨a : Set α, af : Set.Finite a, ta : (t : Set α) ⊆ ⋃ y ∈ a, Metric.eball y (δ / 2)⟩ := - EMetric.totallyBounded_iff.1 this (δ / 2) δpos' - -- replace each center by a nearby approximation in `s`, giving a new set `b` - let b := F '' a - have : b.Finite := af.image _ - have tb : ∀ x ∈ t, ∃ y ∈ b, edist x y < δ := by - intro x hx - rcases mem_iUnion₂.1 (ta hx) with ⟨z, za, Dxz⟩ - exists F z, mem_image_of_mem _ za - calc - edist x (F z) ≤ edist x z + edist z (F z) := edist_triangle _ _ _ - _ < δ / 2 + δ / 2 := ENNReal.add_lt_add Dxz (Fspec z).2 - _ = δ := ENNReal.add_halves _ - -- keep only the points in `b` that are close to point in `t`, yielding a new set `c` - let c := { y ∈ b | ∃ x ∈ t, edist x y < δ } - have : c.Finite := ‹b.Finite›.subset fun x hx => hx.1 - -- points in `t` are well approximated by points in `c` - have tc : ∀ x ∈ t, ∃ y ∈ c, edist x y ≤ δ := by - intro x hx - rcases tb x hx with ⟨y, yv, Dxy⟩ - have : y ∈ c := by simpa [c, -mem_image] using ⟨yv, ⟨x, hx, Dxy⟩⟩ - exact ⟨y, this, le_of_lt Dxy⟩ - -- points in `c` are well approximated by points in `t` - have ct : ∀ y ∈ c, ∃ x ∈ t, edist y x ≤ δ := by - rintro y ⟨_, x, xt, Dyx⟩ - have : edist y x ≤ δ := - calc - edist y x = edist x y := edist_comm _ _ - _ ≤ δ := le_of_lt Dyx - exact ⟨x, xt, this⟩ - -- it follows that their Hausdorff distance is small - have : hausdorffEDist (t : Set α) c ≤ δ := hausdorffEDist_le_of_mem_edist tc ct - have Dtc : hausdorffEDist (t : Set α) c < ε := this.trans_lt δlt - -- the set `c` is not empty, as it is well approximated by a nonempty set - have hc : c.Nonempty := nonempty_of_hausdorffEDist_ne_top t.nonempty (ne_top_of_lt Dtc) - -- let `d` be the version of `c` in the type `NonemptyCompacts α` - let d : NonemptyCompacts α := ⟨⟨c, ‹c.Finite›.isCompact⟩, hc⟩ - have : c ⊆ s := by - intro x hx - rcases (mem_image _ _ _).1 hx.1 with ⟨y, ⟨_, yx⟩⟩ - rw [← yx] - exact (Fspec y).1 - have : d ∈ v := ⟨‹c.Finite›, this⟩ - -- we have proved that `d` is a good approximation of `t` as requested - exact ⟨d, ‹d ∈ v›, Dtc⟩ - UniformSpace.secondCountable_of_separable (NonemptyCompacts α) - theorem isometry_singleton : Isometry ({·} : α → NonemptyCompacts α) := fun _ _ => hausdorffEDist_singleton diff --git a/Mathlib/Topology/MetricSpace/Isometry.lean b/Mathlib/Topology/MetricSpace/Isometry.lean index 0fdbb686f4ca3f..c89bf11c768a84 100644 --- a/Mathlib/Topology/MetricSpace/Isometry.lean +++ b/Mathlib/Topology/MetricSpace/Isometry.lean @@ -121,6 +121,10 @@ protected lemma inr [AddZeroClass α] [AddZeroClass β] : Isometry (AddMonoidHom theorem comp {g : β → γ} {f : α → β} (hg : Isometry g) (hf : Isometry f) : Isometry (g ∘ f) := fun _ _ => (hg _ _).trans (hf _ _) +omit [PseudoEMetricSpace α] in +lemma postcomp_pi [Fintype α] {g : β → γ} (hg : Isometry g) : Isometry (fun f : α → β ↦ g ∘ f) := + fun _ _ ↦ by simp [edist_pi_def, hg.edist_eq] + /-- An isometry from a metric space is a uniform continuous map -/ protected theorem uniformContinuous (hf : Isometry f) : UniformContinuous f := hf.lipschitz.uniformContinuous diff --git a/Mathlib/Topology/MetricSpace/PiNat.lean b/Mathlib/Topology/MetricSpace/PiNat.lean index a074e24c734a3a..61b1c66783476d 100644 --- a/Mathlib/Topology/MetricSpace/PiNat.lean +++ b/Mathlib/Topology/MetricSpace/PiNat.lean @@ -933,21 +933,18 @@ lemma min_dist_le_dist_pi (x y : ∀ i, F i) (i : ι) : lemma dist_le_dist_pi_of_dist_lt (h : dist x y < 2⁻¹ ^ encode i) : dist (x i) (y i) ≤ dist x y := by simpa only [not_le.2 h, false_or] using min_le_iff.1 (min_dist_le_dist_pi x y i) --- TODO: fix two non-terminal simps below; second one uses a long lemma list -set_option linter.flexible false in /-- Given a countable family of metric spaces, one may put a distance on their product `Π i, E i`. It is highly non-canonical, though, and therefore not registered as a global instance. The distance we use here is `dist x y = ∑' i, min (1/2)^(encode i) (dist (x i) (y i))`. -/ @[instance_reducible] protected def pseudoMetricSpace : PseudoMetricSpace (∀ i, F i) := - PseudoEMetricSpace.toPseudoMetricSpaceOfDist dist - (fun x y ↦ by simp [dist_eq_tsum]; positivity) fun x y ↦ by - rw [edist_eq_tsum, dist_eq_tsum, - ENNReal.ofReal_tsum_of_nonneg (fun _ ↦ by positivity) (dist_summable ..)] - simp [edist, ENNReal.inv_pow] - congr! with a - exact PseudoMetricSpace.edist_dist (x a) (y a) + PseudoEMetricSpace.toPseudoMetricSpaceOfDist dist (fun x y ↦ by rw [dist_eq_tsum]; positivity) + fun x y ↦ by + rw [edist_eq_tsum, dist_eq_tsum, + ENNReal.ofReal_tsum_of_nonneg (fun _ ↦ by positivity) (dist_summable ..)] + congr! with a + simp [edist, ENNReal.inv_pow, PseudoMetricSpace.edist_dist (x a) (y a)] end PseudoMetricSpace diff --git a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean index aabb1cb2b5e4d6..0de76b0b857754 100644 --- a/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean +++ b/Mathlib/Topology/MetricSpace/Pseudo/Defs.lean @@ -258,10 +258,10 @@ open Lean Meta Qq Function /-- Extension for the `positivity` tactic: distances are nonnegative. -/ @[positivity Dist.dist _ _] -meta def evalDist : PositivityExt where eval {u α} _zα pα? e := do +meta def evalDist : PositivityExt where eval {u α} _zα pα? e := + match pα? with | none => pure .none | some _ => do match u, α, e with | 0, ~q(ℝ), ~q(@Dist.dist $β $inst $a $b) => - let some _ := pα? | pure .none let _inst ← synthInstanceQ q(PseudoMetricSpace $β) assertInstancesCommute pure (.nonnegative q(dist_nonneg)) diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean b/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean index 7da001d1a1cebe..39cc3938b52177 100644 --- a/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean +++ b/Mathlib/Topology/OpenPartialHomeomorph/Constructions.lean @@ -86,6 +86,11 @@ def prod (eX : OpenPartialHomeomorph X X') (eY : OpenPartialHomeomorph Y Y') : continuousOn_invFun := eX.continuousOn_symm.prodMap eY.continuousOn_symm toPartialEquiv := eX.toPartialEquiv.prod eY.toPartialEquiv +@[deprecated "deprecated in favour of `OpenPartialHomeomorph.prod_toPartialHomeomorph`" + (since := "2026-06-24")] +lemma prod_toPartialEquiv (eX : OpenPartialHomeomorph X X') (eY : OpenPartialHomeomorph Y Y') : + (eX.prod eY).toPartialHomeomorph.toPartialEquiv = eX.toPartialEquiv.prod eY.toPartialEquiv := + rfl @[simp, mfld_simps] theorem prod_symm (eX : OpenPartialHomeomorph X X') (eY : OpenPartialHomeomorph Y Y') : (eX.prod eY).symm = eX.symm.prod eY.symm := diff --git a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean index a8f90bd94cf6df..c9563195b3dc36 100644 --- a/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean +++ b/Mathlib/Topology/OpenPartialHomeomorph/Defs.lean @@ -86,9 +86,11 @@ def Simps.symm_apply (e : OpenPartialHomeomorph X Y) : Y → X := e.symm initialize_simps_projections OpenPartialHomeomorph (toFun → apply, invFun → symm_apply) +@[fun_prop] protected theorem continuousOn : ContinuousOn e e.source := e.continuousOn_toFun +@[fun_prop] theorem continuousOn_symm : ContinuousOn e.symm e.target := e.continuousOn_invFun diff --git a/Mathlib/Topology/Order.lean b/Mathlib/Topology/Order.lean index f87c4a05190af6..56f78491229fab 100644 --- a/Mathlib/Topology/Order.lean +++ b/Mathlib/Topology/Order.lean @@ -292,6 +292,7 @@ theorem closure_indiscrete [IndiscreteTopology α] {s : Set α} (h : s.Nonempty) closure s = Set.univ := Dense.closure_eq (dense_indiscrete h) /-- Every function to the indiscrete topology is continuous -/ +@[fun_prop] theorem continuous_of_indiscreteTopology {β} [TopologicalSpace β] [IndiscreteTopology β] {f : α → β} : Continuous f where isOpen_preimage := by simp [IndiscreteTopology.isOpen_iff] diff --git a/Mathlib/Topology/Order/Basic.lean b/Mathlib/Topology/Order/Basic.lean index 47c36e01f50ace..bee708fa11f2b7 100644 --- a/Mathlib/Topology/Order/Basic.lean +++ b/Mathlib/Topology/Order/Basic.lean @@ -777,25 +777,33 @@ end LinearOrder section ConditionallyCompleteLinearOrder variable {X : Type*} [ConditionallyCompleteLinearOrder X] [TopologicalSpace X] [OrderTopology X] variable {Y : Type*} [ConditionallyCompleteLinearOrder Y] [TopologicalSpace Y] [OrderTopology Y] -variable [DenselyOrdered X] {f : X → Y} {x : X} +variable {f : X → Y} {x : X} /-- An order-theoretically left-continuous function is topologically left-continuous, assuming -the function is between conditionally complete linear orders with order topologies, and the domain -is densely ordered. -/ +the function is between conditionally complete linear orders with order topologies. -/ lemma LeftOrdContinuous.continuousWithinAt_Iic (hf : LeftOrdContinuous f) : ContinuousWithinAt f (Iic x) x := by rw [ContinuousWithinAt, OrderTopology.topology_eq_generate_intervals (α := Y)] simp_rw [TopologicalSpace.tendsto_nhds_generateFrom_iff, mem_nhdsWithin] rintro V ⟨z, rfl | rfl⟩ hxz -- The case `V = Ioi z`. - · obtain ⟨_, ⟨a, hax, rfl⟩, hza⟩ := (lt_isLUB_iff <| hf isLUB_Iio).mp hxz - exact ⟨Ioi a, isOpen_Ioi, hax, fun b hab ↦ hza.trans_le <| hf.mono hab.1.le⟩ + · obtain hz | ne := em' (f ⁻¹' Iic z).Nonempty + · exact ⟨univ, isOpen_univ, mem_univ _, fun a ha ↦ not_le.mp fun h ↦ hz ⟨a, h⟩⟩ + have bdd : BddAbove (f ⁻¹' Iic z) := ⟨x, fun a ha ↦ (hf.mono.reflect_lt (ha.trans_lt hxz)).le⟩ + have u_eq : Ioi (sSup (f ⁻¹' Iic z)) = f ⁻¹' Ioi z := by + refine Set.ext fun a ↦ ⟨fun ha ↦ ?_, fun ha ↦ ?_⟩ + · exact not_le.mp fun h ↦ ha.not_ge (le_csSup bdd h) + · apply lt_of_le_of_ne + · exact csSup_le ne fun b hb ↦ (hf.mono.reflect_lt (hb.trans_lt ha)).le + · have : sSup (f '' f ⁻¹' Iic z) ≤ z := csSup_le (.image _ ne) fun _ ⟨b, hb, heq⟩ ↦ heq ▸ hb + exact fun h ↦ this.not_gt ((h ▸ ha).trans_eq (hf.map_csSup ne bdd)) + exact ⟨f ⁻¹' Ioi z, u_eq ▸ isOpen_Ioi, hxz, fun _ h ↦ h.1⟩ -- The case `V = Iio z`. · exact ⟨univ, isOpen_univ, trivial, fun a ha ↦ (hf.mono ha.2).trans_lt hxz⟩ /-- An order-theoretically right-continuous function is topologically right-continuous, assuming -the function is between conditionally complete linear orders with order topologies, and the domain -is densely ordered. -/ +the function is between conditionally complete linear orders with order topologies. -/ +@[to_dual existing] lemma RightOrdContinuous.continuousWithinAt_Ici (hf : RightOrdContinuous f) : ContinuousWithinAt f (Ici x) x := hf.dual.continuousWithinAt_Iic diff --git a/Mathlib/Topology/Order/Compact.lean b/Mathlib/Topology/Order/Compact.lean index 8f328aaa190157..f0d3709f69b256 100644 --- a/Mathlib/Topology/Order/Compact.lean +++ b/Mathlib/Topology/Order/Compact.lean @@ -543,17 +543,4 @@ theorem le_sSup_image_Icc (h : ContinuousOn f <| Icc a b) (hc : c ∈ Icc a b) : rw [h.image_Icc (hc.1.trans hc.2)] at this exact this.2 -theorem image_Icc_of_monotoneOn (hab : a ≤ b) (h : ContinuousOn f <| Icc a b) - (h' : MonotoneOn f <| Icc a b) : f '' Icc a b = Icc (f a) (f b) := by - rw [h.image_Icc hab] - congr! - · exact h'.sInf_image_Icc hab - · exact h'.sSup_image_Icc hab - -theorem image_Icc_of_antitoneOn (hab : a ≤ b) (h : ContinuousOn f <| Icc a b) - (h' : AntitoneOn f <| Icc a b) : f '' Icc a b = Icc (f b) (f a) := by - have : Icc (f b) (f a) = Icc (toDual (f a)) (toDual (f b)) := by rw [Icc_toDual]; rfl - rw [this] - exact image_Icc_of_monotoneOn (β := βᵒᵈ) hab h h'.dual_right - end ContinuousOn diff --git a/Mathlib/Topology/Order/Completion.lean b/Mathlib/Topology/Order/Completion.lean index 050b3668cc531f..a7521c3468d4dd 100644 --- a/Mathlib/Topology/Order/Completion.lean +++ b/Mathlib/Topology/Order/Completion.lean @@ -63,8 +63,7 @@ namespace Fill instance : TopologicalSpace (Fill α) := Preorder.topology _ -instance [TopologicalSpace α] [OrderTopology α] : OrderTopology (Fill α) := - ⟨rfl⟩ +instance : OrderTopology (Fill α) := ⟨rfl⟩ /-- A continuous embedding of `α` into `Fill α`. -/ def some : α ↪o Fill α where diff --git a/Mathlib/Topology/Order/IntermediateValue.lean b/Mathlib/Topology/Order/IntermediateValue.lean index b42d4b31e012d3..3bcb0694485bac 100644 --- a/Mathlib/Topology/Order/IntermediateValue.lean +++ b/Mathlib/Topology/Order/IntermediateValue.lean @@ -633,6 +633,50 @@ theorem intermediate_value_Ioo' {a b : α} (hab : a ≤ b) {f : α → δ} ((hf.continuousWithinAt ⟨hab, refl b⟩).mono Ioo_subset_Icc_self) ((hf.continuousWithinAt ⟨refl a, hab⟩).mono Ioo_subset_Icc_self) +theorem intermediate_value_Ici {a : α} {f : α → δ} (hf : ContinuousOn f (Ici a)) + (htop : Tendsto f atTop atTop) : Ici (f a) ⊆ f '' Ici a := + isPreconnected_Ici.intermediate_value_Ici self_mem_Ici + (le_principal_iff.mpr (Ici_mem_atTop a)) hf htop + +theorem intermediate_value_Ici' {a : α} {f : α → δ} (hf : ContinuousOn f (Ici a)) + (htop : Tendsto f atTop atBot) : Iic (f a) ⊆ f '' Ici a := + isPreconnected_Ici.intermediate_value_Iic self_mem_Ici + (le_principal_iff.mpr (Ici_mem_atTop a)) hf htop + +theorem intermediate_value_Iic {a : α} {f : α → δ} (hf : ContinuousOn f (Iic a)) + (hbot : Tendsto f atBot atBot) : Iic (f a) ⊆ f '' Iic a := + isPreconnected_Iic.intermediate_value_Iic self_mem_Iic + (le_principal_iff.mpr (Iic_mem_atBot a)) hf hbot + +theorem intermediate_value_Iic' {a : α} {f : α → δ} (hf : ContinuousOn f (Iic a)) + (hbot : Tendsto f atBot atTop) : Ici (f a) ⊆ f '' Iic a := + isPreconnected_Iic.intermediate_value_Ici self_mem_Iic + (le_principal_iff.mpr (Iic_mem_atBot a)) hf hbot + +theorem intermediate_value_Ioi {a : α} {f : α → δ} (hf : ContinuousOn f (Ici a)) + (htop : Tendsto f atTop atTop) : Ioi (f a) ⊆ f '' Ioi a := by + intro y hy + have := intermediate_value_Ici hf htop (mem_Ici_of_Ioi hy) + grind + +theorem intermediate_value_Ioi' {a : α} {f : α → δ} (hf : ContinuousOn f (Ici a)) + (htop : Tendsto f atTop atBot) : Iio (f a) ⊆ f '' Ioi a := by + intro y hy + have := intermediate_value_Ici' hf htop (mem_Iic_of_Iio hy) + grind + +theorem intermediate_value_Iio {a : α} {f : α → δ} (hf : ContinuousOn f (Iic a)) + (hbot : Tendsto f atBot atBot) : Iio (f a) ⊆ f '' Iio a := by + intro y hy + have := intermediate_value_Iic hf hbot (mem_Iic_of_Iio hy) + grind + +theorem intermediate_value_Iio' {a : α} {f : α → δ} (hf : ContinuousOn f (Iic a)) + (hbot : Tendsto f atBot atTop) : Ioi (f a) ⊆ f '' Iio a := by + intro y hy + have := intermediate_value_Iic' hf hbot (mem_Ici_of_Ioi hy) + grind + /-- **Intermediate value theorem**: if `f` is continuous on an order-connected set `s` and `a`, `b` are two points of this set, then `f` sends `s` to a superset of `Icc (f a) (f b)`. -/ theorem ContinuousOn.surjOn_Icc {s : Set α} [hs : OrdConnected s] {f : α → δ} @@ -678,6 +722,10 @@ theorem ContinuousOn.surjOn_of_tendsto' {f : α → δ} {s : Set α} [OrdConnect (htop : Tendsto (fun x : s => f x) atTop atBot) : SurjOn f s univ := ContinuousOn.surjOn_of_tendsto (δ := δᵒᵈ) hs hf hbot htop +/-! +### Monotonicity of injective continuous functions +-/ + theorem Continuous.strictMono_of_inj_boundedOrder [BoundedOrder α] {f : α → δ} (hf_c : Continuous f) (hf : f ⊥ ≤ f ⊤) (hf_i : Injective f) : StrictMono f := by intro a b hab @@ -787,3 +835,112 @@ theorem ContinuousOn.strictMonoOn_of_injOn_Ioo {a b : α} {f : α → δ} (hab : have : StrictMono g ∨ StrictAnti g := Continuous.strictMono_of_inj hf_c.restrict hf_i.injective exact this.imp strictMono_restrict.mp strictAntiOn_iff_strictAnti.mpr + +/-! +### Images of continuous monotone functions +-/ + +variable {a b : α} {f : α → δ} + +theorem ContinuousOn.image_Icc_of_monotoneOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : MonotoneOn f (Icc a b)) : f '' Icc a b = Icc (f a) (f b) := + subset_antisymm (hmono.image_Icc_subset) (intermediate_value_Icc hab hf) + +theorem ContinuousOn.image_Icc_of_antitoneOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : AntitoneOn f (Icc a b)) : f '' Icc a b = Icc (f b) (f a) := + subset_antisymm (hmono.image_Icc_subset) (intermediate_value_Icc' hab hf) + +theorem ContinuousOn.image_Ico_of_strictMonoOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : StrictMonoOn f (Icc a b)) : f '' Ico a b = Ico (f a) (f b) := + subset_antisymm (hmono.image_Ico_subset) (intermediate_value_Ico hab hf) + +theorem ContinuousOn.image_Ico_of_strictAntiOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : StrictAntiOn f (Icc a b)) : f '' Ico a b = Ioc (f b) (f a) := + subset_antisymm (hmono.image_Ico_subset) (intermediate_value_Ico' hab hf) + +theorem ContinuousOn.image_Ioc_of_strictMonoOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : StrictMonoOn f (Icc a b)) : f '' Ioc a b = Ioc (f a) (f b) := + subset_antisymm (hmono.image_Ioc_subset) (intermediate_value_Ioc hab hf) + +theorem ContinuousOn.image_Ioc_of_strictAntiOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : StrictAntiOn f (Icc a b)) : f '' Ioc a b = Ico (f b) (f a) := + subset_antisymm (hmono.image_Ioc_subset) (intermediate_value_Ioc' hab hf) + +theorem ContinuousOn.image_Ioo_of_strictMonoOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : StrictMonoOn f (Icc a b)) : f '' Ioo a b = Ioo (f a) (f b) := + subset_antisymm (hmono.image_Ioo_subset) (intermediate_value_Ioo hab hf) + +theorem ContinuousOn.image_Ioo_of_strictAntiOn (hab : a ≤ b) (hf : ContinuousOn f (Icc a b)) + (hmono : StrictAntiOn f (Icc a b)) : f '' Ioo a b = Ioo (f b) (f a) := + subset_antisymm (hmono.image_Ioo_subset) (intermediate_value_Ioo' hab hf) + +theorem ContinuousOn.image_uIcc_of_monotoneOn (hf : ContinuousOn f [[a, b]]) + (hmono : MonotoneOn f [[a, b]]) : f '' [[a, b]] = [[f a, f b]] := + subset_antisymm (hmono.image_uIcc_subset) (intermediate_value_uIcc hf) + +theorem ContinuousOn.image_uIcc_of_antitoneOn (hf : ContinuousOn f [[a, b]]) + (hmono : AntitoneOn f [[a, b]]) : f '' [[a, b]] = [[f a, f b]] := + subset_antisymm (hmono.image_uIcc_subset) (intermediate_value_uIcc hf) + +theorem ContinuousOn.image_Ici_of_monotoneOn (hf : ContinuousOn f (Ici a)) + (hmono : MonotoneOn f (Ici a)) (htop : Tendsto f atTop atTop) : f '' Ici a = Ici (f a) := + subset_antisymm (hmono.image_Ici_subset) (intermediate_value_Ici hf htop) + +theorem ContinuousOn.image_Ici_of_antitoneOn (hf : ContinuousOn f (Ici a)) + (hmono : AntitoneOn f (Ici a)) (htop : Tendsto f atTop atBot) : f '' Ici a = Iic (f a) := + subset_antisymm (hmono.image_Ici_subset) (intermediate_value_Ici' hf htop) + +theorem ContinuousOn.image_Iic_of_monotoneOn (hf : ContinuousOn f (Iic a)) + (hmono : MonotoneOn f (Iic a)) (hbot : Tendsto f atBot atBot) : f '' Iic a = Iic (f a) := + subset_antisymm (hmono.image_Iic_subset) (intermediate_value_Iic hf hbot) + +theorem ContinuousOn.image_Iic_of_antitoneOn (hf : ContinuousOn f (Iic a)) + (hmono : AntitoneOn f (Iic a)) (hbot : Tendsto f atBot atTop) : f '' Iic a = Ici (f a) := + subset_antisymm (hmono.image_Iic_subset) (intermediate_value_Iic' hf hbot) + +theorem ContinuousOn.image_Ioi_of_strictMonoOn (hf : ContinuousOn f (Ici a)) + (hmono : StrictMonoOn f (Ici a)) (htop : Tendsto f atTop atTop) : f '' Ioi a = Ioi (f a) := + subset_antisymm (hmono.image_Ioi_subset) (intermediate_value_Ioi hf htop) + +theorem ContinuousOn.image_Ioi_of_strictAntiOn (hf : ContinuousOn f (Ici a)) + (hmono : StrictAntiOn f (Ici a)) (htop : Tendsto f atTop atBot) : f '' Ioi a = Iio (f a) := + subset_antisymm (hmono.image_Ioi_subset) (intermediate_value_Ioi' hf htop) + +theorem ContinuousOn.image_Iio_of_strictMonoOn (hf : ContinuousOn f (Iic a)) + (hmono : StrictMonoOn f (Iic a)) (hbot : Tendsto f atBot atBot) : f '' Iio a = Iio (f a) := + subset_antisymm (hmono.image_Iio_subset) (intermediate_value_Iio hf hbot) + +theorem ContinuousOn.image_Iio_of_strictAntiOn (hf : ContinuousOn f (Iic a)) + (hmono : StrictAntiOn f (Iic a)) (hbot : Tendsto f atBot atTop) : f '' Iio a = Ioi (f a) := + subset_antisymm (hmono.image_Iio_subset) (intermediate_value_Iio' hf hbot) + +/-! +### Order-agnostic images under continuous strictly monotone maps + +If `f` is *globally* continuous and strictly monotone, each interval maps to the interval of its +endpoint images with no `a ≤ b` hypothesis: when `a > b`, both sides are empty (since `f a > f b`). +-/ + +theorem Continuous.image_Icc_of_strictMono (hf_c : Continuous f) (hf : StrictMono f) : + f '' Icc a b = Icc (f a) (f b) := by + rcases le_or_gt a b with hab | hab + · exact hf_c.continuousOn.image_Icc_of_monotoneOn hab (hf.monotone.monotoneOn _) + · simp [not_le.mpr hab, not_le.mpr (hf hab)] + +theorem Continuous.image_Ico_of_strictMono (hf_c : Continuous f) (hf : StrictMono f) : + f '' Ico a b = Ico (f a) (f b) := by + rcases le_or_gt a b with hab | hab + · exact hf_c.continuousOn.image_Ico_of_strictMonoOn hab (hf.strictMonoOn _) + · simp [lt_asymm hab, lt_asymm (hf hab)] + +theorem Continuous.image_Ioc_of_strictMono (hf_c : Continuous f) (hf : StrictMono f) : + f '' Ioc a b = Ioc (f a) (f b) := by + rcases le_or_gt a b with hab | hab + · exact hf_c.continuousOn.image_Ioc_of_strictMonoOn hab (hf.strictMonoOn _) + · simp [lt_asymm hab, lt_asymm (hf hab)] + +theorem Continuous.image_Ioo_of_strictMono (hf_c : Continuous f) (hf : StrictMono f) : + f '' Ioo a b = Ioo (f a) (f b) := by + rcases le_or_gt a b with hab | hab + · exact hf_c.continuousOn.image_Ioo_of_strictMonoOn hab (hf.strictMonoOn _) + · simp [lt_asymm hab, lt_asymm (hf hab)] diff --git a/Mathlib/Topology/Order/LeftRightLim.lean b/Mathlib/Topology/Order/LeftRightLim.lean index e423bb8009f047..349daa40030a6a 100644 --- a/Mathlib/Topology/Order/LeftRightLim.lean +++ b/Mathlib/Topology/Order/LeftRightLim.lean @@ -64,18 +64,17 @@ noncomputable def Function.rightLim (f : α → β) (a : α) : β := open Function theorem leftLim_eq_of_tendsto [hα : TopologicalSpace α] [h'α : OrderTopology α] [T2Space β] - {f : α → β} {a : α} {y : β} (h : 𝓝[<] a ≠ ⊥) (h' : Tendsto f (𝓝[<] a) (𝓝 y)) : + {f : α → β} {a : α} {y : β} [h : (𝓝[<] a).NeBot] (h' : Tendsto f (𝓝[<] a) (𝓝 y)) : leftLim f a = y := by have h'' : ∃ y, Tendsto f (𝓝[<] a) (𝓝 y) := ⟨y, h'⟩ rw [h'α.topology_eq_generate_intervals] at h h' h'' - simp only [leftLim, h, h'', not_true, or_self_iff, if_false] - haveI := neBot_iff.2 h + simp only [leftLim, neBot_iff.mp h, h'', not_true, or_self_iff, if_false] exact lim_eq h' theorem rightLim_eq_of_tendsto [TopologicalSpace α] [OrderTopology α] [T2Space β] - {f : α → β} {a : α} {y : β} (h : 𝓝[>] a ≠ ⊥) (h' : Tendsto f (𝓝[>] a) (𝓝 y)) : + {f : α → β} {a : α} {y : β} [h : (𝓝[>] a).NeBot] (h' : Tendsto f (𝓝[>] a) (𝓝 y)) : Function.rightLim f a = y := - leftLim_eq_of_tendsto (α := αᵒᵈ) h h' + leftLim_eq_of_tendsto (α := αᵒᵈ) (h := h) h' theorem leftLim_eq_of_eq_bot [hα : TopologicalSpace α] [h'α : OrderTopology α] (f : α → β) {a : α} (h : 𝓝[<] a = ⊥) : leftLim f a = f a := by @@ -111,9 +110,9 @@ theorem rightLim_eq_of_isTop {f : α → β} {a : α} (ha : IsTop a) : theorem ContinuousWithinAt.leftLim_eq [TopologicalSpace α] [OrderTopology α] [T2Space β] {f : α → β} {a : α} (hf : ContinuousWithinAt f (Iic a) a) : leftLim f a = f a := by - rcases eq_or_ne (𝓝[<] a) ⊥ with h' | h' + rcases eq_or_neBot (𝓝[<] a) with h' | h' · simp [leftLim_eq_of_eq_bot f h'] - apply leftLim_eq_of_tendsto h' + apply leftLim_eq_of_tendsto exact hf.tendsto.mono_left (nhdsWithin_mono _ Iio_subset_Iic_self) theorem ContinuousWithinAt.rightLim_eq [TopologicalSpace α] [OrderTopology α] [T2Space β] @@ -195,7 +194,7 @@ theorem leftLim_rightLim [TopologicalSpace α] [OrderTopology α] [T3Space β] {f : α → β} {a : α} (h : Tendsto f (𝓝[<] a) (𝓝 (f.leftLim a))) [h' : (𝓝[<] a).NeBot] : f.rightLim.leftLim a = f.leftLim a := by obtain ⟨b, hb⟩ : (Iio a).Nonempty := Filter.nonempty_of_mem (self_mem_nhdsWithin (a := a)) - apply leftLim_eq_of_tendsto (neBot_iff.mp h') + apply leftLim_eq_of_tendsto apply (closed_nhds_basis (f.leftLim a)).tendsto_right_iff.2 rintro s ⟨s_mem, s_closed⟩ obtain ⟨u, au, hu⟩ : ∃ u, u < a ∧ Ioo u a ⊆ {x | f x ∈ s} := by @@ -273,24 +272,23 @@ variable {α β : Type*} [LinearOrder α] [ConditionallyCompleteLinearOrder β] [OrderTopology β] {f : α → β} (hf : Monotone f) {x y : α} include hf -theorem leftLim_eq_sSup [TopologicalSpace α] [OrderTopology α] (h : 𝓝[<] x ≠ ⊥) : +theorem leftLim_eq_sSup [TopologicalSpace α] [OrderTopology α] [(𝓝[<] x).NeBot] : leftLim f x = sSup (f '' Iio x) := - leftLim_eq_of_tendsto h (hf.tendsto_nhdsLT x) + leftLim_eq_of_tendsto (hf.tendsto_nhdsLT x) -theorem rightLim_eq_sInf [TopologicalSpace α] [OrderTopology α] (h : 𝓝[>] x ≠ ⊥) : +theorem rightLim_eq_sInf [TopologicalSpace α] [OrderTopology α] [(𝓝[>] x).NeBot] : rightLim f x = sInf (f '' Ioi x) := - rightLim_eq_of_tendsto h (hf.tendsto_nhdsGT x) + rightLim_eq_of_tendsto (hf.tendsto_nhdsGT x) theorem leftLim_le (h : x ≤ y) : leftLim f x ≤ f y := by letI : TopologicalSpace α := Preorder.topology α haveI : OrderTopology α := ⟨rfl⟩ - rcases eq_or_ne (𝓝[<] x) ⊥ with (h' | h') + rcases eq_or_neBot (𝓝[<] x) with h' | h' · simpa [leftLim, h'] using hf h - haveI A : NeBot (𝓝[<] x) := neBot_iff.2 h' - rw [leftLim_eq_sSup hf h'] + rw [leftLim_eq_sSup hf] refine csSup_le ?_ ?_ · simp only [image_nonempty] - exact (forall_mem_nonempty_iff_neBot.2 A) _ self_mem_nhdsWithin + exact (forall_mem_nonempty_iff_neBot.2 h') _ self_mem_nhdsWithin · simp only [mem_image, mem_Iio, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂] intro z hz exact hf (hz.le.trans h) @@ -298,10 +296,10 @@ theorem leftLim_le (h : x ≤ y) : leftLim f x ≤ f y := by theorem le_leftLim (h : x < y) : f x ≤ leftLim f y := by letI : TopologicalSpace α := Preorder.topology α haveI : OrderTopology α := ⟨rfl⟩ - rcases eq_or_ne (𝓝[<] y) ⊥ with (h' | h') + rcases eq_or_neBot (𝓝[<] y) with h' | h' · rw [leftLim_eq_of_eq_bot _ h'] exact hf h.le - rw [leftLim_eq_sSup hf h'] + rw [leftLim_eq_sSup hf] refine le_csSup ⟨f y, ?_⟩ (mem_image_of_mem _ h) simp only [upperBounds, mem_image, mem_Iio, forall_exists_index, and_imp, forall_apply_eq_imp_iff₂, mem_setOf_eq] @@ -356,9 +354,8 @@ theorem tendsto_rightLim_within (x : α) : Tendsto f (𝓝[>] x) (𝓝[≥] righ coincides with the value of the function. -/ theorem continuousWithinAt_Iio_iff_leftLim_eq : ContinuousWithinAt f (Iio x) x ↔ leftLim f x = f x := by - rcases eq_or_ne (𝓝[<] x) ⊥ with (h' | h') + rcases eq_or_neBot (𝓝[<] x) with h' | h' · simp [leftLim_eq_of_eq_bot f h', ContinuousWithinAt, h'] - haveI : (𝓝[Iio x] x).NeBot := neBot_iff.2 h' refine ⟨fun h => tendsto_nhds_unique (hf.tendsto_leftLim x) h.tendsto, fun h => ?_⟩ have := hf.tendsto_leftLim x rwa [h] at this diff --git a/Mathlib/Topology/Order/LeftRightNhds.lean b/Mathlib/Topology/Order/LeftRightNhds.lean index 5925877c50128b..6d518605ac27d8 100644 --- a/Mathlib/Topology/Order/LeftRightNhds.lean +++ b/Mathlib/Topology/Order/LeftRightNhds.lean @@ -78,6 +78,18 @@ theorem nhdsGT_basis_of_exists_gt {a : α} (h : ∃ b, a < b) : (𝓝[>] a).HasB lemma nhdsGT_basis [NoMaxOrder α] (a : α) : (𝓝[>] a).HasBasis (a < ·) (Ioo a) := nhdsGT_basis_of_exists_gt <| exists_gt a +lemma nhdsGT_basis_Ioc_of_exists_gt [DenselyOrdered α] {a : α} (h : ∃ b, a < b) : + (𝓝[>] a).HasBasis (fun x ↦ a < x) (Ioc a) := + nhdsGT_basis_of_exists_gt h |>.to_hasBasis' + (fun _ hac ↦ + have ⟨b, hab, hbc⟩ := exists_between hac + ⟨b, hab, Ioc_subset_Ioo_right hbc⟩) + fun _ hac ↦ mem_of_superset ((nhdsGT_basis_of_exists_gt h).mem_of_mem hac) Ioo_subset_Ioc_self + +lemma nhdsGT_basis_Ioc [DenselyOrdered α] [NoMaxOrder α] (a : α) : + (𝓝[>] a).HasBasis (fun x ↦ a < x) (Ioc a) := + nhdsGT_basis_Ioc_of_exists_gt <| exists_gt a + theorem nhdsGT_eq_bot_iff {a : α} : 𝓝[>] a = ⊥ ↔ IsTop a ∨ ∃ b, a ⋖ b := by by_cases ha : IsTop a · simp [ha, ha.isMax.Ioi_eq] @@ -210,6 +222,18 @@ theorem nhdsLT_basis_of_exists_lt {a : α} (h : ∃ b, b < a) : (𝓝[<] a).HasB theorem nhdsLT_basis [NoMinOrder α] (a : α) : (𝓝[<] a).HasBasis (· < a) (Ioo · a) := nhdsLT_basis_of_exists_lt <| exists_lt a +lemma nhdsLT_basis_Ico_of_exists_lt [DenselyOrdered α] {a : α} (h : ∃ b, b < a) : + (𝓝[<] a).HasBasis (· < a) (Ico · a) := + nhdsLT_basis_of_exists_lt h |>.to_hasBasis' + (fun _ hac ↦ + have ⟨b, hab, hbc⟩ := exists_between hac + ⟨b, hbc, Ico_subset_Ioo_left hab⟩) + fun _ hac ↦ mem_of_superset ((nhdsLT_basis_of_exists_lt h).mem_of_mem hac) Ioo_subset_Ico_self + +lemma nhdsLT_basis_Ico [DenselyOrdered α] [NoMinOrder α] (a : α) : + (𝓝[<] a).HasBasis (· < a) (Ico · a) := + nhdsLT_basis_Ico_of_exists_lt <| exists_lt a + theorem nhdsLT_eq_bot_iff {a : α} : 𝓝[<] a = ⊥ ↔ IsBot a ∨ ∃ b, b ⋖ a := by convert! (config := { preTransparency := .default }) nhdsGT_eq_bot_iff (a := OrderDual.toDual a) using 4 diff --git a/Mathlib/Topology/Order/OrderClosed.lean b/Mathlib/Topology/Order/OrderClosed.lean index 6fe8f916cb1921..6388ad265d4940 100644 --- a/Mathlib/Topology/Order/OrderClosed.lean +++ b/Mathlib/Topology/Order/OrderClosed.lean @@ -48,6 +48,8 @@ see their statements. * `le_of_tendsto`, `ge_of_tendsto` : if `f` converges to `a` and eventually `f x ≤ b` (resp., `b ≤ f x`), then `a ≤ b` (resp., `b ≤ a`); we also provide primed versions that assume the inequalities to hold for all `x`. +* `monotone_of_frequently_monotone_of_tendsto`, `antitone_of_frequently_antitone_of_tendsto` : the + pointwise limit of frequently monotone or antitone functions is monotone or antitone. ### Min, max, `sSup` and `sInf` @@ -514,25 +516,54 @@ theorem IsClosed.epigraph [TopologicalSpace β] {f : β → α} {s : Set β} (hs (hf : ContinuousOn f s) : IsClosed { p : β × α | p.1 ∈ s ∧ f p.1 ≤ p.2 } := (hs.preimage continuous_fst).isClosed_le (hf.comp continuousOn_fst Subset.rfl) continuousOn_snd +section Tendsto + +variable {ι : Type*} {l : Filter ι} [Preorder β] {F : ι → β → α} {f : β → α} {s : Set β} + +/-- The limit of a collection of functions that is frequently monotone on a set is monotone on +that set. -/ +lemma monotoneOn_of_frequently_monotoneOn_of_tendsto (hF : ∃ᶠ i in l, MonotoneOn (F i) s) + (hlim : ∀ x ∈ s, Tendsto (fun i ↦ F i x) l (𝓝 (f x))) : MonotoneOn f s := + fun a ha b hb hab ↦ le_of_tendsto_of_tendsto_of_frequently (hlim a ha) (hlim b hb) <| + hF.mono fun _ hi ↦ hi ha hb hab + +/-- The limit of a collection of functions that is frequently monotone is monotone. -/ +lemma monotone_of_frequently_monotone_of_tendsto (hF : ∃ᶠ i in l, Monotone (F i)) + (hlim : ∀ x, Tendsto (fun i ↦ F i x) l (𝓝 (f x))) : Monotone f := + monotoneOn_univ.1 <| monotoneOn_of_frequently_monotoneOn_of_tendsto + (hF.mono fun _ hi ↦ hi.monotoneOn _) fun x _ ↦ hlim x + +/-- The limit of a collection of functions that is frequently antitone on a set is antitone on +that set. -/ +lemma antitoneOn_of_frequently_antitoneOn_of_tendsto (hF : ∃ᶠ i in l, AntitoneOn (F i) s) + (hlim : ∀ x ∈ s, Tendsto (fun i ↦ F i x) l (𝓝 (f x))) : AntitoneOn f s := + monotoneOn_of_frequently_monotoneOn_of_tendsto (α := αᵒᵈ) hF hlim + +/-- The limit of a collection of functions that is frequently antitone is antitone. -/ +lemma antitone_of_frequently_antitone_of_tendsto (hF : ∃ᶠ i in l, Antitone (F i)) + (hlim : ∀ x, Tendsto (fun i ↦ F i x) l (𝓝 (f x))) : Antitone f := + monotone_of_frequently_monotone_of_tendsto (α := αᵒᵈ) hF hlim + /-- The set of monotone functions on a set is closed. -/ -theorem isClosed_monotoneOn [Preorder β] {s : Set β} : IsClosed {f : β → α | MonotoneOn f s} := by +theorem isClosed_monotoneOn : IsClosed {f : β → α | MonotoneOn f s} := by simp only [isClosed_iff_clusterPt, clusterPt_principal_iff_frequently] - intro g hg a ha b hb hab - have hmain (x) : Tendsto (fun f' ↦ f' x) (𝓝 g) (𝓝 (g x)) := continuousAt_apply x _ - exact le_of_tendsto_of_tendsto_of_frequently (hmain a) (hmain b) (hg.mono fun g h ↦ h ha hb hab) + exact fun g hg => monotoneOn_of_frequently_monotoneOn_of_tendsto hg + fun x _ ↦ continuousAt_apply x g /-- The set of monotone functions is closed. -/ -theorem isClosed_monotone [Preorder β] : IsClosed {f : β → α | Monotone f} := by +theorem isClosed_monotone : IsClosed {f : β → α | Monotone f} := by simp_rw [← monotoneOn_univ] exact isClosed_monotoneOn /-- The set of antitone functions on a set is closed. -/ -theorem isClosed_antitoneOn [Preorder β] {s : Set β} : IsClosed {f : β → α | AntitoneOn f s} := - isClosed_monotoneOn (α := αᵒᵈ) (β := β) +theorem isClosed_antitoneOn : IsClosed {f : β → α | AntitoneOn f s} := + isClosed_monotoneOn (α := αᵒᵈ) /-- The set of antitone functions is closed. -/ -theorem isClosed_antitone [Preorder β] : IsClosed {f : β → α | Antitone f} := - isClosed_monotone (α := αᵒᵈ) (β := β) +theorem isClosed_antitone : IsClosed {f : β → α | Antitone f} := + isClosed_monotone (α := αᵒᵈ) + +end Tendsto end Preorder diff --git a/Mathlib/Topology/Order/ScottTopology.lean b/Mathlib/Topology/Order/ScottTopology.lean index 583db890a253ff..5e77f371f9f738 100644 --- a/Mathlib/Topology/Order/ScottTopology.lean +++ b/Mathlib/Topology/Order/ScottTopology.lean @@ -101,8 +101,7 @@ and closed sets are `DirSupClosedOn D`. -/ class IsScottHausdorff (α) (D : Set (Set α)) [Preorder α] [TopologicalSpace α] : Prop where topology_eq_scottHausdorff : ‹TopologicalSpace α› = scottHausdorff α D -instance (α) (D : Set (Set α)) [Preorder α] [TopologicalSpace α] : - @IsScottHausdorff α D _ (scottHausdorff α D) := +instance (α) (D : Set (Set α)) [Preorder α] : @IsScottHausdorff α D _ (scottHausdorff α D) := @IsScottHausdorff.mk _ _ _ (scottHausdorff α D) rfl namespace IsScottHausdorff diff --git a/Mathlib/Topology/PartialHomeomorph/Defs.lean b/Mathlib/Topology/PartialHomeomorph/Defs.lean index ab78f5be70780c..3cd827e68cc315 100644 --- a/Mathlib/Topology/PartialHomeomorph/Defs.lean +++ b/Mathlib/Topology/PartialHomeomorph/Defs.lean @@ -86,9 +86,11 @@ def Simps.symm_apply (e : PartialHomeomorph X Y) : Y → X := e.symm initialize_simps_projections PartialHomeomorph (toFun → apply, invFun → symm_apply) +@[fun_prop] protected theorem continuousOn : ContinuousOn e e.source := e.continuousOn_toFun +@[fun_prop] theorem continuousOn_symm : ContinuousOn e.symm e.target := e.continuousOn_invFun diff --git a/Mathlib/Topology/Semicontinuity/Hemicontinuity.lean b/Mathlib/Topology/Semicontinuity/Hemicontinuity.lean index 4f232c3b43625d..7e3e18d8a77616 100644 --- a/Mathlib/Topology/Semicontinuity/Hemicontinuity.lean +++ b/Mathlib/Topology/Semicontinuity/Hemicontinuity.lean @@ -10,7 +10,6 @@ public import Mathlib.Topology.NhdsWithin public import Mathlib.Topology.Separation.Regular public import Mathlib.Topology.Defs.Sequences import Mathlib.Topology.Sequences -import Mathlib.Topology.ContinuousOn /-! # Hemicontinuity @@ -63,7 +62,7 @@ alias ⟨UpperHemicontinuous.forall_isOpen, UpperHemicontinuous.of_forall_isOpen /-! ### Characterization in terms of preimages of intervals of sets -/ lemma upperHemicontinuousWithinAt_iff_preimage_Iic : - UpperHemicontinuousWithinAt f s x ↔ ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' (Iic u) ∈ 𝓝[s] x := by + UpperHemicontinuousWithinAt f s x ↔ ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' Iic u ∈ 𝓝[s] x := by simp_rw [upperHemicontinuousWithinAt_iff] rw [hasBasis_nhdsSet (f x) |>.forall_iff ?h₁, hasBasis_nhdsSet (f x) |>.forall_iff ?h₂] case h₂ => @@ -77,24 +76,24 @@ lemma upperHemicontinuousWithinAt_iff_preimage_Iic : simp [hu.mem_nhdsSet, eventually_iff, Iic] lemma upperHemicontinuousAt_iff_preimage_Iic : - UpperHemicontinuousAt f x ↔ ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' (Iic u) ∈ 𝓝 x := by + UpperHemicontinuousAt f x ↔ ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' Iic u ∈ 𝓝 x := by simpa [upperHemicontinuousWithinAt_univ_iff] using upperHemicontinuousWithinAt_iff_preimage_Iic (s := univ) lemma upperHemicontinuousOn_iff_preimage_Iic : - UpperHemicontinuousOn f s ↔ ∀ x ∈ s, ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' (Iic u) ∈ 𝓝[s] x := by + UpperHemicontinuousOn f s ↔ ∀ x ∈ s, ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' Iic u ∈ 𝓝[s] x := by simp [upperHemicontinuousOn_iff, upperHemicontinuousWithinAt_iff_preimage_Iic] lemma upperHemicontinuous_iff_preimage_Iic : - UpperHemicontinuous f ↔ ∀ x, ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' (Iic u) ∈ 𝓝 x := by + UpperHemicontinuous f ↔ ∀ x, ∀ u ∈ 𝓝ˢ (f x), f ⁻¹' Iic u ∈ 𝓝 x := by simp [upperHemicontinuous_iff, upperHemicontinuousAt_iff_preimage_Iic] /-- A correspondence `f : α → Set β` is upper hemicontinuous if and only if its *upper inverse* (i.e., `u : Set β ↦ f ⁻¹' (Iic u)`, note that `f ⁻¹' (Iic u) = {x | f x ⊆ u}`) sends open sets to open sets. -/ lemma upperHemicontinuous_iff_isOpen_preimage_Iic : - UpperHemicontinuous f ↔ ∀ u, IsOpen u → IsOpen (f ⁻¹' (Iic u)) := by - simp_rw [upperHemicontinuous_iff_preimage_Iic, isOpen_iff_mem_nhds (s := f ⁻¹' (Iic _))] + UpperHemicontinuous f ↔ ∀ u, IsOpen u → IsOpen (f ⁻¹' Iic u) := by + simp_rw [upperHemicontinuous_iff_preimage_Iic, isOpen_iff_mem_nhds (s := f ⁻¹' Iic _)] conv => enter [1, x] rw [hasBasis_nhdsSet (f x) |>.forall_iff <| @@ -105,7 +104,7 @@ lemma upperHemicontinuous_iff_isOpen_preimage_Iic : (i.e., `u : Set β ↦ (f ⁻¹' (Iic uᶜ))ᶜ`, note that `f ⁻¹' (Iic u) = {x | (f x ∩ u).Nonempty}`) sends closed sets to closed sets. -/ lemma upperHemicontinuous_iff_isClosed_compl_preimage_Iic_compl : - UpperHemicontinuous f ↔ ∀ u, IsClosed u → IsClosed (f ⁻¹' (Iic uᶜ))ᶜ := by + UpperHemicontinuous f ↔ ∀ u, IsClosed u → IsClosed (f ⁻¹' Iic uᶜ)ᶜ := by conv_rhs => rw [compl_surjective.forall] simp [← isOpen_compl_iff] @@ -116,21 +115,25 @@ lemma isClosedMap_iff_upperHemicontinuous {f : α → β} : rw [isClosedMap_iff_kernImage, upperHemicontinuous_iff_isOpen_preimage_Iic] aesop +lemma lowerHemicontinuous_iff_isOpen_inter_nonempty : + LowerHemicontinuous f ↔ ∀ u, IsOpen u → IsOpen {x | (f x ∩ u).Nonempty} := by + simp_rw [lowerHemicontinuous_iff, lowerHemicontinuousAt_iff, isOpen_iff_mem_nhds, + forall_comm (α := α), mem_setOf, Filter.Eventually] + /-- A correspondence `f : α → Set β` is lower hemicontinuous if and only if its *lower inverse* (i.e., `u : Set β ↦ (f ⁻¹' (Iic uᶜ))ᶜ`, note that `f ⁻¹' (Iic u) = {x | (f x ∩ u).Nonempty}`) sends open sets to open sets. -/ lemma lowerHemicontinuous_iff_isOpen_compl_preimage_Iic_compl : - LowerHemicontinuous f ↔ ∀ u, IsOpen u → IsOpen (f ⁻¹' (Iic uᶜ))ᶜ := by + LowerHemicontinuous f ↔ ∀ u, IsOpen u → IsOpen (f ⁻¹' Iic uᶜ)ᶜ := by have (u : Set β) : (f ⁻¹' (Iic uᶜ))ᶜ = {x | (f x ∩ u).Nonempty} := by simp [Set.ext_iff, Iic, Set.mem_compl_iff, Set.not_subset, Set.Nonempty] - simp_rw [lowerHemicontinuous_iff, lowerHemicontinuousAt_iff, this, isOpen_iff_mem_nhds, - forall_comm (α := α), mem_setOf, Filter.Eventually] + simpa [this] using lowerHemicontinuous_iff_isOpen_inter_nonempty /-- A correspondence `f : α → Set β` is lower hemicontinuous if and only if its *upper inverse* (i.e., `u : Set β ↦ f ⁻¹' (Iic u)`, note that `f ⁻¹' (Iic u) = {x | f x ⊆ u}`) sends closed sets to closed sets. -/ lemma lowerHemicontinuous_iff_isClosed_preimage_Iic : - LowerHemicontinuous f ↔ ∀ u, IsClosed u → IsClosed (f ⁻¹' (Iic u)) := by + LowerHemicontinuous f ↔ ∀ u, IsClosed u → IsClosed (f ⁻¹' Iic u) := by conv_rhs => rw [compl_surjective.forall] simp [← isOpen_compl_iff] @@ -141,13 +144,24 @@ lemma isOpenMap_iff_lowerHemicontinuous {f : α → β} : rw [isOpenMap_iff_kernImage, lowerHemicontinuous_iff_isClosed_preimage_Iic] aesop -/-! ### Singleton maps -/ +section singleton_maps + +/-! ### Singleton maps + +Functions `f : α → β` are continuous if and only if they are lower hemicontinuous if and only if +they are upper hemicontinuous. This is in the sense that the map `g : α → Set β` given by +`g x = {f x}` is both lower or upper hemicontinuous. + +This section also provides dot notation to access this fact for continuous functions. +-/ + +variable {f : α → β} {s : Set α} {x : α} lemma upperHemicontinuous_singleton_id : UpperHemicontinuous ({·} : α → Set α) := by simp [upperHemicontinuous_iff, upperHemicontinuousAt_iff] @[simp] -lemma upperHemicontinuousWithinAt_singleton_iff {f : α → β} {s : Set α} {x : α} : +lemma upperHemicontinuousWithinAt_singleton_iff : UpperHemicontinuousWithinAt ({f ·}) s x ↔ ContinuousWithinAt f s x := by refine ⟨?_, fun hf ↦ upperHemicontinuous_singleton_id.upperHemicontinuousWithinAt _ _ |>.comp hf (mapsTo_image _ _)⟩ @@ -157,21 +171,68 @@ lemma upperHemicontinuousWithinAt_singleton_iff {f : α → β} {s : Set α} {x filter_upwards [h t ht] with x exact mem_of_mem_nhds +alias ⟨_, ContinuousWithinAt.upperHemicontinuousWithinAt⟩ := + upperHemicontinuousWithinAt_singleton_iff + @[simp] -lemma upperHemicontinuousAt_singleton_iff {f : α → β} {x : α} : +lemma upperHemicontinuousAt_singleton_iff : UpperHemicontinuousAt ({f ·}) x ↔ ContinuousAt f x := by simp [← upperHemicontinuousWithinAt_univ_iff, continuousWithinAt_univ] +alias ⟨_, ContinuousAt.upperHemicontinuousAt⟩ := upperHemicontinuousAt_singleton_iff + @[simp] -lemma upperHemicontinuousOn_singleton_iff {f : α → β} {s : Set α} : +lemma upperHemicontinuousOn_singleton_iff : UpperHemicontinuousOn ({f ·}) s ↔ ContinuousOn f s := forall₂_congr <| fun _ _ ↦ upperHemicontinuousWithinAt_singleton_iff +alias ⟨_, ContinuousOn.upperHemicontinuousOn⟩ := upperHemicontinuousOn_singleton_iff + @[simp] -lemma upperHemicontinuous_singleton_iff {f : α → β} : +lemma upperHemicontinuous_singleton_iff : UpperHemicontinuous ({f ·}) ↔ Continuous f := by simp [← upperHemicontinuousOn_univ_iff] +alias ⟨_, Continuous.upperHemicontinuous⟩ := upperHemicontinuous_singleton_iff + +lemma lowerHemicontinuous_singleton_id : LowerHemicontinuous ({·} : α → Set α) := by + intro x t ⟨ht, hne⟩ + filter_upwards [ht.mem_nhds (Set.singleton_inter_nonempty.mp hne)] with x' hx' + exact ⟨ht, Set.singleton_inter_nonempty.mpr hx'⟩ + +@[simp] +lemma lowerHemicontinuousWithinAt_singleton_iff : + LowerHemicontinuousWithinAt ({f ·}) s x ↔ ContinuousWithinAt f s x := by + refine ⟨?_, fun hf ↦ (lowerHemicontinuous_singleton_id.lowerHemicontinuousWithinAt _ _).comp + hf (mapsTo_image _ _)⟩ + simp only [lowerHemicontinuousWithinAt_iff, Set.singleton_inter_nonempty, + ContinuousWithinAt, tendsto_iff_forall_eventually_mem] + intro h t ht + obtain ⟨u, hut, huo, hux⟩ := mem_nhds_iff.mp ht + exact (h u huo hux).mono fun _ hx' ↦ hut hx' + +alias ⟨_, ContinuousWithinAt.lowerHemicontinuousWithinAt⟩ := + lowerHemicontinuousWithinAt_singleton_iff + +@[simp] +lemma lowerHemicontinuousAt_singleton_iff : LowerHemicontinuousAt ({f ·}) x ↔ ContinuousAt f x := by + simp [← lowerHemicontinuousWithinAt_univ_iff, continuousWithinAt_univ] + +alias ⟨_, ContinuousAt.lowerHemicontinuousAt⟩ := lowerHemicontinuousAt_singleton_iff + +@[simp] +lemma lowerHemicontinuousOn_singleton_iff : LowerHemicontinuousOn ({f ·}) s ↔ ContinuousOn f s := + forall₂_congr <| fun _ _ ↦ lowerHemicontinuousWithinAt_singleton_iff + +alias ⟨_, ContinuousOn.lowerHemicontinuousOn⟩ := lowerHemicontinuousOn_singleton_iff + +@[simp] +lemma lowerHemicontinuous_singleton_iff : LowerHemicontinuous ({f ·}) ↔ Continuous f := by + simp [← lowerHemicontinuousOn_univ_iff] + +alias ⟨_, Continuous.lowerHemicontinuous⟩ := lowerHemicontinuous_singleton_iff + +end singleton_maps /-! ### Union and intersection, and post-composition with the preimage map -/ @@ -280,8 +341,7 @@ end Inducing The more general fact is that if `f` is upper hemicontinuous at `x₀` within `s`, and if `x₀` is a cluster point of `s ∩ {x | (f x).Nonempty}`, then `(f x₀).Nonempty`. -/ -lemma UpperHemicontinuous.isClosed_domain {α β : Type*} [TopologicalSpace α] - [TopologicalSpace β] {f : α → Set β} (hf : UpperHemicontinuous f) : +lemma UpperHemicontinuous.isClosed_domain (hf : UpperHemicontinuous f) : IsClosed {x | (f x).Nonempty} := by simp only [← isOpen_compl_iff, compl_setOf, not_nonempty_iff_eq_empty, isOpen_iff_mem_nhds] intro x (hx : f x = ∅) @@ -297,8 +357,7 @@ of sequences `x : ℕ → α` and `y : ℕ → β` such that `x` tends to `x₀` set containing all `f x'` for `x'` sufficiently close to `x`. This is a partial converse of `UpperHemicontinuousAt.mem_of_tendsto`. -/ -lemma UpperHemicontinuousAt.of_sequences {α β : Type*} [TopologicalSpace α] - [TopologicalSpace β] {f : α → Set β} {x₀ : α} [(𝓝 x₀).IsCountablyGenerated] +lemma UpperHemicontinuousAt.of_sequences {x₀ : α} [(𝓝 x₀).IsCountablyGenerated] {K : Set β} (hK : IsSeqCompact K) (hf : ∀ᶠ x in 𝓝 x₀, f x ⊆ K) (h : ∀ x : ℕ → α, Tendsto x atTop (𝓝 x₀) → ∀ y : ℕ → β, (∀ n, y n ∈ f (x n)) → ∀ y₀, Tendsto y atTop (𝓝 y₀) → y₀ ∈ f x₀) : @@ -319,9 +378,8 @@ closed, then for any sequences `x` and `y` (in `α` and `β`, respectively) tend respectively, if `y n ∈ f (x n)` frequently, then `y₀ ∈ f x₀`. This is a partial converse of `UpperHemicontinuousAt.of_sequences`. -/ -lemma UpperHemicontinuousAt.mem_of_tendsto {α β ι : Type*} [TopologicalSpace α] - [TopologicalSpace β] [RegularSpace β] {f : α → Set β} {x₀ : α} {l : Filter ι} - (hf : UpperHemicontinuousAt f x₀) (hf_closed : IsClosed (f x₀)) +lemma UpperHemicontinuousAt.mem_of_tendsto {ι : Type*} [RegularSpace β] {x₀ : α} + {l : Filter ι} (hf : UpperHemicontinuousAt f x₀) (hf_closed : IsClosed (f x₀)) {x : ι → α} (hx : Tendsto x l (𝓝 x₀)) {y : ι → β} (hy : ∃ᶠ n in l, y n ∈ f (x n)) {y₀ : β} (hy₀ : Tendsto y l (𝓝 y₀)) : y₀ ∈ f x₀ := by @@ -335,3 +393,39 @@ lemma UpperHemicontinuousAt.mem_of_tendsto {α β ι : Type*} [TopologicalSpace filter_upwards [hx (hf s hs)] with n hn hyn simp only [← subset_interior_iff_mem_nhdsSet, preimage_setOf_eq, mem_setOf_eq] at hn exact interior_subset <| hn hyn + +/-! ### Open lower sections -/ + +omit [TopologicalSpace β] in +/-- A correspondence `f : α → Set β` has open lower sections if and only if its *lower inverse* +(i.e., `b : β ↦ (f ⁻¹' (Iic {b}ᶜ))ᶜ = {x | b ∈ f x}`) sends every point to an open set. -/ +lemma hasOpenLowerSections_iff_isOpen_compl_preimage_Iic_compl : + HasOpenLowerSections f ↔ ∀ b, IsOpen (f ⁻¹' Iic {b}ᶜ)ᶜ := by + have h (b : β) : (f ⁻¹' (Iic {b}ᶜ))ᶜ = {x | b ∈ f x} := by + simp [Set.ext_iff, Iic, Set.mem_compl_iff] + simp_rw [h, hasOpenLowerSections_iff_isOpen] + +omit [TopologicalSpace β] in +/-- A correspondence `f : α → Set β` has open lower sections if and only if its *upper inverse* +(i.e., `b : β ↦ f ⁻¹' (Iic {b}ᶜ) = {x | b ∉ f x}`) sends every point to a closed set. -/ +lemma hasOpenLowerSections_iff_isClosed_preimage_Iic : + HasOpenLowerSections f ↔ ∀ b, IsClosed (f ⁻¹' Iic {b}ᶜ) := by + simp_rw [← isOpen_compl_iff] + exact hasOpenLowerSections_iff_isOpen_compl_preimage_Iic_compl + +/-! ### Open Graphs -/ + +/-- A lower hemicontinuous function intersected with a function with an open graph is lower +hemicontinuous. -/ +lemma LowerHemicontinuous.inter_hasOpenCGraph {f g : α → Set β} + (hf : LowerHemicontinuous f) (hg : HasOpenCGraph g) : + LowerHemicontinuous (fun x ↦ f x ∩ g x) := by + simp_rw [lowerHemicontinuous_iff_isOpen_inter_nonempty] at ⊢ hf + intro t ht + rw [isOpen_iff_forall_mem_open] + intro x ⟨y, ⟨hyf, hyg⟩, hyt⟩ + obtain ⟨U, V, hU, hV, hxU, hyV, hUV⟩ := (isOpen_prod_iff.mp hg) x y hyg + refine ⟨U ∩ {x' | (f x' ∩ (t ∩ V)).Nonempty}, ?_, hU.inter (hf _ (ht.inter hV)), + ⟨hxU, y, hyf, hyt, hyV⟩⟩ + intro x' ⟨hx'U, z, hzf, hzt, hzV⟩ + exact ⟨z, ⟨hzf, hUV (Set.mk_mem_prod hx'U hzV)⟩, hzt⟩ diff --git a/Mathlib/Topology/Semicontinuous.lean b/Mathlib/Topology/Semicontinuous.lean deleted file mode 100644 index bf09eea3bd4835..00000000000000 --- a/Mathlib/Topology/Semicontinuous.lean +++ /dev/null @@ -1,8 +0,0 @@ -module -- shake: keep-all - -import Mathlib.Algebra.Order.Module.Field -import Mathlib.Data.EReal.Operations -import Mathlib.Topology.Algebra.InfiniteSum.Order -import Mathlib.Topology.MetricSpace.Bounded - -deprecated_module (since := "2025-12-17") diff --git a/Mathlib/Topology/Separation/Hausdorff.lean b/Mathlib/Topology/Separation/Hausdorff.lean index 934cdd1aa08abf..bdcb3685f9d283 100644 --- a/Mathlib/Topology/Separation/Hausdorff.lean +++ b/Mathlib/Topology/Separation/Hausdorff.lean @@ -675,7 +675,7 @@ theorem Continuous.isClosedEmbedding [CompactSpace X] [T2Space Y] {f : X → Y} .of_continuous_injective_isClosedMap h hf h.isClosedMap /-- A continuous surjective map from a compact space to a Hausdorff space is a quotient map. -/ -theorem IsQuotientMap.of_surjective_continuous [CompactSpace X] [T2Space Y] {f : X → Y} +theorem Topology.IsQuotientMap.of_surjective_continuous [CompactSpace X] [T2Space Y] {f : X → Y} (hsurj : Surjective f) (hcont : Continuous f) : IsQuotientMap f := hcont.isClosedMap.isQuotientMap hcont hsurj diff --git a/Mathlib/Topology/Sets/BaseChangeNhds.lean b/Mathlib/Topology/Sets/BaseChangeNhds.lean new file mode 100644 index 00000000000000..83a8c9c091db3d --- /dev/null +++ b/Mathlib/Topology/Sets/BaseChangeNhds.lean @@ -0,0 +1,97 @@ +/- +Copyright (c) 2026 Yannis Monbru-Carcelero. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Yannis Monbru Carcelero +-/ + +module + +public import Mathlib.CategoryTheory.Filtered.Final +public import Mathlib.Topology.Sets.Compacts + +/-! +# Base changes among different families of neighbourhoods + +This file builds base changes for `.compactsInside`, `openNhds`. + +It also contains the evidences that `openRcNhds_to_openNhds`and + `openRcNhds_to_compactNhds` are initials functors. + +-/ + +@[expose] public section + +namespace TopologicalSpace + +open Set CategoryTheory Limits + +variable {α : Type*} [TopologicalSpace α] + +namespace Opens + +/-- For `U` an open subset included in a open subset `V`, there is +a map sending compacts inside `U` to the ones inside `V` -/ +def baseChangeCompactsInside {U V : Opens α} (h : U ⟶ V) : U.compactsInside → V.compactsInside := + fun ⟨K, hK⟩ ↦ ⟨K, fun _ hx ↦ Set.mem_of_subset_of_mem (leOfHom h) (hK hx)⟩ + +lemma baseChangeCompactsInside_mono {U V : Opens α} (h : U ⟶ V) : + Monotone <| baseChangeCompactsInside h := + fun _ _ hKL _ hx ↦ SetLike.mem_coe.mpr (hKL hx) + +@[simp] +lemma baseChangeCompactsInside_comp {U V W : Opens α} (h : U ⟶ V) (k : V ⟶ W) + (K : U.compactsInside) : + baseChangeCompactsInside (h ≫ k) K = baseChangeCompactsInside k (baseChangeCompactsInside h K) + := by rfl + +@[simp] +lemma baseChangeOpenNhds_id {U : Opens α} (K : U.compactsInside) : + baseChangeCompactsInside (𝟙 U) K = K := by rfl + +end Opens + +namespace Compacts + +/-- For `K` a compact subset included in a compact subset `L`, there +is a map sending open neighbourhoods of `L` to the ones of `K` -/ +def baseChangeOpenNhds {K L : Compacts α} (h : K ⟶ L) : L.openNhds → K.openNhds := + fun ⟨U, hU⟩ ↦ ⟨U, fun _ hx ↦ Set.mem_of_subset_of_mem hU (leOfHom h hx)⟩ + +lemma baseChangeOpenNhds_mono {K L : Compacts α} (h : K ⟶ L) : Monotone <| baseChangeOpenNhds h := + fun _ _ hUV _ hx ↦ SetLike.mem_coe.mpr (hUV hx) + +@[simp] +lemma baseChangeOpenNhds_comp {K L M : Compacts α} (h : K ⟶ L) (k : L ⟶ M) (U : M.openNhds) : + baseChangeOpenNhds (h ≫ k) U = baseChangeOpenNhds h (baseChangeOpenNhds k U) := by rfl + +@[simp] +lemma baseChangeOpenNhds_id {K : Compacts α} (U : K.openNhds) : + baseChangeOpenNhds (𝟙 K) U = U := by rfl + +/-- The evidence that `⊥` is initial among the compact open neighbourhoods of `⊥` -/ +def isInitialBotOpensOpenNhdsBot : IsInitial (⊥ : (⊥ : Compacts α).openNhds) := .ofUniqueHom + (fun _ ↦ homOfLE <| by tauto) + (fun _ _ ↦ eq_of_comp_right_eq <| by tauto) + +instance {K : Compacts α} [T2Space α] [LocallyCompactSpace α] : + K.openRcNhdsToOpenNhds_mono.functor.Initial := by + rw [Monotone.initial_functor_iff] + intro U + obtain ⟨L, h1, h2, h3⟩ := exists_compact_between K.isCompact U.val.isOpen U.property + use ⟨⟨interior L, isOpen_interior⟩, + ⟨IsCompact.of_isClosed_subset h1 isClosed_closure + (closure_minimal interior_subset (IsCompact.isClosed h1)), + h2⟩⟩ + exact Subset.trans interior_subset h3 + +instance {K : Compacts α} [T2Space α] : K.openRcNhdsToCompactNhds_mono.functor.Initial := by + rw [Monotone.initial_functor_iff] + intro L + obtain ⟨U, h1, h2⟩ := exists_open_set_nhds_of_compactsNhds L + have h3 : closure (U : Set α) ⊆ L := (IsClosed.closure_subset_iff + (IsCompact.isClosed L.1.isCompact') ).2 h2 + exact ⟨⟨U, ⟨ IsCompact.of_isClosed_subset L.1.isCompact' isClosed_closure h3, h1⟩⟩, h3⟩ + +end Compacts + +end TopologicalSpace diff --git a/Mathlib/Topology/Sets/Compacts.lean b/Mathlib/Topology/Sets/Compacts.lean index 79a5b1cbe6bcea..358da018fa882e 100644 --- a/Mathlib/Topology/Sets/Compacts.lean +++ b/Mathlib/Topology/Sets/Compacts.lean @@ -273,8 +273,113 @@ theorem singleton_prod_singleton (x : α) (y : β) : -- todo: add `pi` +open Topology + +/-- The compacts neigbourhoods of a compact -/ +def compactNhds (K : Compacts α) : Set (Compacts α) := + {K' | ∀ (x : K), (K': Set α) ∈ 𝓝 x.val} + +lemma subset_of_mem_compactNhds {K K' : Compacts α} (h : K' ∈ K.compactNhds) : + (K : Set α) ⊆ K' := + fun x hx ↦ mem_of_mem_nhds (h ⟨x, hx⟩) + +lemma exists_open_set_nhds_of_compactsNhds {K : Compacts α} (L : K.compactNhds) : + ∃ U : Opens α, (K : Set α) ⊆ U ∧ (U : Set α) ⊆ L := by + obtain ⟨U, KsubU, openU, UsubL⟩ := exists_open_set_nhds (fun x hx ↦ L.2 ⟨x, hx⟩) + exact ⟨⟨U, openU⟩, KsubU, UsubL⟩ + +lemma exists_open_set_nhds_of_mem_compactsNhds {K K' : Compacts α} (h : K' ∈ K.compactNhds) : + ∃ U : Opens α, (K : Set α) ⊆ U ∧ (U : Set α) ⊆ K' := + exists_open_set_nhds_of_compactsNhds ⟨K', h⟩ + +/-- The compact neigbourhood induced by the existence of an open subset between two compacts -/ +def compactNhdsMkOfOpens {K : Compacts α} (L : Compacts α) (U : Opens α) + (h1 : (K : Set α) ⊆ U) (h2 : (U : Set α) ⊆ L) : + K.compactNhds := + ⟨L, fun _ ↦ Filter.mem_of_superset (IsOpen.mem_nhds U.is_open' (h1 (Subtype.coe_prop _))) h2⟩ + +instance [T2Space α] (K : Compacts α) : SemilatticeInf (K.compactNhds) where + inf L M := ⟨L.1 ⊓ M.1, fun x ↦ Filter.inter_mem_iff.2 ⟨L.2 x, M.2 x⟩⟩ + inf_le_right _ _ := Subtype.coe_le_coe.mp inf_le_right + inf_le_left _ _:= Subtype.coe_le_coe.mp inf_le_left + le_inf _ _ _ h k := + Subtype.coe_le_coe.mp (le_inf (Subtype.coe_le_coe.mpr h) (Subtype.coe_le_coe.mpr k)) + +/-- The set of opens neighbourhood of a compact subset -/ +def openNhds (K : Compacts α) : Set (Opens α) := {U | (K : Set α) ⊆ U} + +instance (K : Compacts α) : IsCodirectedOrder K.openNhds where + directed U1 U2 := ⟨⟨U1.val ⊓ U2.val, Set.subset_inter U1.property U2.property⟩, + ⟨Subtype.mk_le_mk.2 inf_le_left, Subtype.mk_le_mk.2 inf_le_right⟩⟩ + +instance (K : Compacts α) : Top K.openNhds := ⟨⊤, Set.subset_univ _⟩ +-- in particular `K.openNhds` is not empty and thus the induced catgory is cofiltered + +instance : Bot (⊥ : Compacts α).openNhds := ⟨⊥, fun _ h ↦ h⟩ + +/-- The opens neighbourhood of a compact subset that are relatively compact -/ +def openRcNhds (K : Compacts α) : Set (Opens α) := + {U | IsCompact (closure (U : Set α )) ∧ (K : Set α) ⊆ U} + +lemma subset_of_mem_openRcNhds {K : Compacts α} {U : Opens α} (h : U ∈ K.openRcNhds) : + (K : Set α) ⊆ U := + fun _ hx ↦ h.right hx + +lemma isCompact_closure_of_mem_openRcNhds {K : Compacts α} {U : Opens α} (h : U ∈ K.openRcNhds) : + IsCompact (closure (U : Set α)) := h.left + +lemma closure_mem_compactNhds_of_mem_openRcNhds {K : Compacts α} {U : Opens α} + (h : U ∈ K.openRcNhds) : + ⟨closure (U : Set α), isCompact_closure_of_mem_openRcNhds h⟩ ∈ K.compactNhds := by + intro x + have H : (U : Set α) ∈ 𝓝 (x : α) := + U.isOpen.mem_nhds <| Compacts.subset_of_mem_openRcNhds h (by simp) + exact Filter.mem_of_superset H subset_closure + +/-- The converting map from relatively compact opens +neighbourhood of a compact subset to its opens neighbourhoods -/ +def openRcNhdsToOpenNhds (K : Compacts α) : K.openRcNhds → K.openNhds := + fun U ↦ ⟨_, U.property.2⟩ + +lemma openRcNhdsToOpenNhds_mono (K : Compacts α) : + Monotone K.openRcNhdsToOpenNhds := fun _ _ h ↦ h + +/-- An open relatively compact neighbourhood of `K` induces a compact neighbourhood by taking +the closure +-/ +def openRcNhdsToCompactNhds (K : Compacts α) : K.openRcNhds → K.compactNhds := + fun U ↦ ⟨_, closure_mem_compactNhds_of_mem_openRcNhds (Subtype.coe_prop U)⟩ + +lemma openRcNhdsToCompactNhds_mono (K : Compacts α) : Monotone K.openRcNhdsToCompactNhds := + fun _ _ h ↦ closure_mono h + +instance [T2Space α] (K : Compacts α) : IsCodirectedOrder K.openRcNhds where + directed U1 U2 := ⟨⟨U1 ⊓ U2, (isCompact_closure_of_mem_openRcNhds (Subtype.coe_prop U1) |>.inter + <| isCompact_closure_of_mem_openRcNhds U2.coe_prop).of_isClosed_subset + isClosed_closure <| closure_inter_subset_inter_closure .., + le_inf (subset_of_mem_openRcNhds (Subtype.coe_prop U1)) + <| subset_of_mem_openRcNhds (Subtype.coe_prop U2)⟩, + Subtype.coe_le_coe.mp inf_le_left, + Subtype.coe_le_coe.mp inf_le_right⟩ + end Compacts +namespace Opens + +/-- The set of compacts inside an open subset -/ +def compactsInside (U : Opens α) : Set (Compacts α) := {K | (K : Set α) ⊆ U} + +/-- For `K` a compact subset insde an open subset `U`, `U` has a structure of open neighbourhood +of `K` -/ +def openNhdsOfCompactsInside {U : Opens α} (K : U.compactsInside) : (K.val).openNhds := + ⟨U, K.property⟩ + +end Opens + +/-- For `U` an open neighbourhood of `K`, `K` has a structure of compact insde `U` -/ +def Compacts.compactsInsideOfOpenNhds {K : Compacts α} (U : K.openNhds) : (U.val).compactsInside := + ⟨K, U.property⟩ + /-! ### Nonempty compact sets -/ /-- The type of nonempty compact sets of a topological space. -/ diff --git a/Mathlib/Topology/Sets/OpenCover.lean b/Mathlib/Topology/Sets/OpenCover.lean index e821d5edeb2b03..b6b104943bea7c 100644 --- a/Mathlib/Topology/Sets/OpenCover.lean +++ b/Mathlib/Topology/Sets/OpenCover.lean @@ -61,6 +61,14 @@ lemma isTopologicalBasis (hu : IsOpenCover u) IsTopologicalBasis (⋃ i, (Subtype.val '' ·) '' B i) := isTopologicalBasis_of_cover (fun i ↦ (u i).2) hu.iSup_set_eq_univ hB +lemma exists_finite_of_compactSpace (hu : IsOpenCover u) [CompactSpace X] : + ∃ (s : Finset ι), IsOpenCover (fun i : s ↦ u i.1) := by + rw [IsOpenCover, eq_top_iff, ← SetLike.coe_subset_coe] at hu + obtain ⟨s, hs⟩ := IsCompact.elim_finite_subcover isCompact_univ _ (fun i ↦ (u i).2) + (by simpa using hu) + use s + simpa [IsOpenCover, eq_top_iff, ← SetLike.coe_subset_coe, Set.iUnion_subtype] using hs + end IsOpenCover lemma Opens.IsBasis.isOpenCover {S : Set (Opens X)} (hS : Opens.IsBasis S) : diff --git a/Mathlib/Topology/Sets/Opens.lean b/Mathlib/Topology/Sets/Opens.lean index 13e02d1439fc4c..c6593fd745e47b 100644 --- a/Mathlib/Topology/Sets/Opens.lean +++ b/Mathlib/Topology/Sets/Opens.lean @@ -5,6 +5,7 @@ Authors: Johannes Hölzl, Mario Carneiro, Floris van Doorn -/ module +public import Mathlib.Data.Fintype.Option public import Mathlib.Order.Hom.CompleteLattice public import Mathlib.Topology.Compactness.Bases public import Mathlib.Topology.ContinuousMap.Basic @@ -49,6 +50,7 @@ We define order structures on both `Opens α` (`CompleteLattice`, `Frame`) and ` @[expose] public section +universe u open Filter Function Order Set @@ -231,6 +233,13 @@ instance [Nonempty α] : Nontrivial (Opens α) where theorem coe_iSup {ι} (s : ι → Opens α) : ((⨆ i, s i : Opens α) : Set α) = ⋃ i, s i := by simp [iSup] +lemma coe_iInf {ι : Type*} [Finite ι] (U : ι → TopologicalSpace.Opens α) : + (((⨅ i, U i) : Opens α) : Set α) = ⋂ i, U i := by + induction ι using Finite.induction_empty_option with + | of_equiv e ih => rw [← e.iInf_comp, ← e.surjective.iInter_comp, ih] + | h_empty => simp + | h_option ih => rw [iInf_option, Set.iInter_option, Opens.coe_inf, ih] + theorem iSup_def {ι} (s : ι → Opens α) : ⨆ i, s i = ⟨⋃ i, s i, isOpen_iUnion fun i => (s i).2⟩ := ext <| coe_iSup s @@ -345,6 +354,27 @@ theorem isBasis_iff_cover {B : Set (Opens α)} : rcases mem_sSup.1 hx with ⟨U, Us, xU⟩ exact ⟨U, hUs Us, xU, le_sSup Us⟩ +lemma IsBasis.exists_iSup_eq {X : Type u} [TopologicalSpace X] {ι : Type*} + {U : ι → TopologicalSpace.Opens X} (hU : TopologicalSpace.Opens.IsBasis (Set.range U)) + (W : TopologicalSpace.Opens X) : ∃ (κ : Type u) (a : κ → ι), W = ⨆ (k : κ), U (a k) := by + obtain ⟨Us, hsub, hUs⟩ := Opens.isBasis_iff_cover.mp hU W + choose a ha using hsub + use Us, fun i ↦ a i.2 + simp [hUs, ha, sSup_eq_iSup' Us] + +lemma IsBasis.exists_iSup_eq_of_isCompact {X : Type u} [TopologicalSpace X] {ι : Type*} + {U : ι → TopologicalSpace.Opens X} (hU : TopologicalSpace.Opens.IsBasis (Set.range U)) + (W : TopologicalSpace.Opens X) (hW : IsCompact W.1) : + ∃ (κ : Type u) (_ : Finite κ) (a : κ → ι), W = ⨆ (k : κ), U (a k) := by + obtain ⟨κ, a, heq⟩ := hU.exists_iSup_eq W + obtain ⟨s, hs⟩ := hW.elim_finite_subcover _ (fun k : κ ↦ (U (a k)).2) (by simp [heq]) + use s, s.finite_toSet, a ∘ Subtype.val + refine le_antisymm ?_ ?_ + · simpa [← SetLike.coe_subset_coe, Set.iUnion_subtype] + · rw [heq, iSup_le_iff] + intro i + exact le_iSup_of_le _ le_rfl + /-- If `α` has a basis consisting of compact opens, then an open set in `α` is compact open iff it is a finite union of some elements in the basis -/ theorem IsBasis.isCompact_open_iff_eq_finite_iUnion {ι : Type*} (b : ι → Opens α) diff --git a/Mathlib/Topology/Sets/VietorisTopology.lean b/Mathlib/Topology/Sets/VietorisTopology.lean index 808104e7ce3d9b..812e1615152237 100644 --- a/Mathlib/Topology/Sets/VietorisTopology.lean +++ b/Mathlib/Topology/Sets/VietorisTopology.lean @@ -37,7 +37,7 @@ incompatible with the Vietoris topology. open Set Topology -variable {α β : Type*} [TopologicalSpace α] [TopologicalSpace β] {f : α → β} +variable {α β γ : Type*} [TopologicalSpace α] [TopologicalSpace β] [TopologicalSpace γ] {f : α → β} namespace TopologicalSpace @@ -465,10 +465,17 @@ theorem continuous_prod : Continuous fun p : Compacts α × Compacts β => p.1 (isOpen_inter_nonempty_of_isOpen hV).prod (isOpen_inter_nonempty_of_isOpen hW), ⟨x, hx, hxV⟩, ⟨y, hy, hyW⟩⟩ -@[fun_prop] theorem _root_.Continuous.compacts_map (hf : Continuous f) : Continuous (Compacts.map f hf) := isEmbedding_coe.continuous_iff.mpr <| hf.image_vietoris.comp continuous_coe +@[fun_prop] +theorem _root_.Continuous.compacts_map' {f : α → Compacts β} {g : α → β → γ} + (hf : Continuous f) (hg : Continuous g.uncurry) : + Continuous (fun x => (f x).map (g x) (by fun_prop)) := by + conv in Compacts.map _ _ _ => equals ({x} ×ˢ f x).map g.uncurry hg => ext; simp + have := hg.compacts_map + fun_prop + @[fun_prop] theorem _root_.Topology.IsInducing.compacts_map (hf : IsInducing f) : IsInducing (Compacts.map f hf.continuous) := @@ -553,6 +560,15 @@ theorem regularSpace_iff : RegularSpace (Compacts α) ↔ RegularSpace α := theorem t3Space_iff : T3Space (Compacts α) ↔ T3Space α := ⟨fun _ => isEmbedding_singleton.t3Space, fun _ => inferInstance⟩ +instance [SecondCountableTopology α] : SecondCountableTopology (Compacts α) := by + obtain ⟨b, hb₁, -, hb₂⟩ := exists_countable_basis α + exact hb₂.compacts.secondCountableTopology <| (countable_setOf_finite_subset hb₁).image _ + +@[simp] +theorem secondCountableTopology_iff : + SecondCountableTopology (Compacts α) ↔ SecondCountableTopology α := + ⟨fun _ => isEmbedding_singleton.secondCountableTopology, fun _ => inferInstance⟩ + theorem isCompact_subsets_of_isCompact {K : Set α} (hK : IsCompact K) : IsCompact {L : Compacts α | ↑L ⊆ K} := by rw [isEmbedding_coe.isCompact_iff] @@ -751,11 +767,17 @@ theorem continuous_prod : simp_rw [isEmbedding_toCompacts.continuous_iff, Function.comp_def, toCompacts_prod] fun_prop -@[fun_prop] theorem _root_.Continuous.nonemptyCompacts_map (hf : Continuous f) : Continuous (NonemptyCompacts.map f hf) := isEmbedding_toCompacts.continuous_iff.mpr <| hf.compacts_map.comp continuous_toCompacts +@[fun_prop] +theorem _root_.Continuous.nonemptyCompacts_map' {f : α → NonemptyCompacts β} {g : α → β → γ} + (hf : Continuous f) (hg : Continuous g.uncurry) : + Continuous (fun x => (f x).map (g x) (by fun_prop)) := by + simp_rw [isEmbedding_toCompacts.continuous_iff, Function.comp_def, toCompacts_map] + fun_prop + @[fun_prop] theorem _root_.Topology.IsInducing.nonemptyCompacts_map (hf : IsInducing f) : IsInducing (NonemptyCompacts.map f hf.continuous) := @@ -806,6 +828,14 @@ theorem regularSpace_iff : RegularSpace (NonemptyCompacts α) ↔ RegularSpace theorem t3Space_iff : T3Space (NonemptyCompacts α) ↔ T3Space α := ⟨fun _ => isEmbedding_singleton.t3Space, fun _ => inferInstance⟩ +instance [SecondCountableTopology α] : SecondCountableTopology (NonemptyCompacts α) := + isEmbedding_toCompacts.secondCountableTopology + +@[simp] +theorem secondCountableTopology_iff : + SecondCountableTopology (NonemptyCompacts α) ↔ SecondCountableTopology α := + ⟨fun _ => isEmbedding_singleton.secondCountableTopology, fun _ => inferInstance⟩ + instance [CompactSpace α] : CompactSpace (NonemptyCompacts α) := isClosedEmbedding_toCompacts.compactSpace diff --git a/Mathlib/Topology/Sheaves/Abelian.lean b/Mathlib/Topology/Sheaves/Abelian.lean index 4b6fd98bf3aefd..7a9b0d10e6652d 100644 --- a/Mathlib/Topology/Sheaves/Abelian.lean +++ b/Mathlib/Topology/Sheaves/Abelian.lean @@ -13,6 +13,7 @@ public import Mathlib.Topology.Sheaves.Skyscraper /-! # Sheaves over Abelian categories + We provide instances for categories of sheaves over Abelian categories. ## Main Results diff --git a/Mathlib/Topology/Sheaves/Module.lean b/Mathlib/Topology/Sheaves/Module.lean new file mode 100644 index 00000000000000..4e7f8a2f554a7c --- /dev/null +++ b/Mathlib/Topology/Sheaves/Module.lean @@ -0,0 +1,50 @@ +/- +Copyright (c) 2026 Andrew Yang. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Andrew Yang +-/ +module + +public import Mathlib.Algebra.Category.ModuleCat.Sheaf.PushforwardContinuous +public import Mathlib.Algebra.Category.Ring.Limits +public import Mathlib.Topology.Sheaves.Over +public import Mathlib.Topology.Sheaves.SheafCondition.Sites + +/-! # Specialized results for sheaves of modules over topological spaces -/ + +@[expose] public section + +noncomputable section + +open CategoryTheory + +universe w v u + +namespace TopologicalSpace.Opens + +variable {X : TopCat.{u}} (U : Opens X) (R : X.Sheaf RingCat.{v}) + +set_option backward.defeqAttrib.useBackward true in +set_option backward.isDefEq.respectTransparency false in +/-- Sheaves of modules over `R.over U` are equivalent to sheaves of modules over `R |_ U`. -/ +def sheafOfModulesEquivOver : + SheafOfModules.{w} (R.over U) ≌ SheafOfModules.{w} (U.sheafRestrict.obj R) := by + refine SheafOfModules.pushforwardPushforwardEquivalence (eqv := U.overEquivalence.symm) + (U.overPullbackSheafEquivOver.app _).inv (U.sheafRestrictSheafEquivOver.app _).inv rfl ?_ + ext : 2 + simp [overPullbackSheafEquivOver, sheafRestrictSheafEquivOver, eqToHom_map, overEquivalence, + IsOpenMap.functor] + +/-- `sheafOfModulesEquivOver` takes `R.over U` to `R |_ U`. -/ +def sheafOfModulesEquivOverUnit (R : X.Sheaf RingCat.{u}) : + (U.sheafOfModulesEquivOver R).functor.obj (SheafOfModules.unit.{u} _) ≅ + SheafOfModules.unit.{u} _ := .refl _ + +/-- `sheafOfModulesEquivOver.inverse` takes `R |_ U` to `R.over U`. -/ +def sheafOfModulesEquivOverInverseUnit (R : X.Sheaf RingCat.{u}) : + (U.sheafOfModulesEquivOver R).inverse.obj (SheafOfModules.unit.{u} _) ≅ + SheafOfModules.unit.{u} _ := + (U.sheafOfModulesEquivOver R).inverse.mapIso (U.sheafOfModulesEquivOverUnit R).symm ≪≫ + ((U.sheafOfModulesEquivOver R).unitIso.app _).symm + +end TopologicalSpace.Opens diff --git a/Mathlib/Topology/Sheaves/Over.lean b/Mathlib/Topology/Sheaves/Over.lean index 9979a573d2427b..b8e74aa67472db 100644 --- a/Mathlib/Topology/Sheaves/Over.lean +++ b/Mathlib/Topology/Sheaves/Over.lean @@ -5,8 +5,10 @@ Authors: Joël Riou -/ module -public import Mathlib.Topology.Sets.Opens public import Mathlib.CategoryTheory.Comma.Over.Basic +public import Mathlib.CategoryTheory.Sites.Over +public import Mathlib.Topology.Sets.Opens +public import Mathlib.Topology.Sheaves.SheafCondition.Sites /-! # Opens and Over categories @@ -15,11 +17,7 @@ In this file, given a topological space `X`, and `U : Opens X`, we show that the category `Over U` (whose objects are the `V : Opens X` equipped with a morphism `V ⟶ U`) is equivalent to the category `Opens U`. - -## TODO -* show that both functors of the equivalence `overEquivalence U` are continuous and - induce an equivalence between `Sheaf ((Opens.grothendieckTopology X).over U) A` - and `Sheaf (Opens.grothendieckTopology U) A` for any category `A`. +This equivalence is bi-continuous, and thus induces an equivalence of sheaf categories. -/ @@ -31,7 +29,7 @@ open CategoryTheory Topology namespace TopologicalSpace -variable {X : Type u} [TopologicalSpace X] (U : Opens X) +variable {X : Type u} [TopologicalSpace X] (U : Opens X) {A : Type*} [Category* A] namespace Opens @@ -54,6 +52,53 @@ def overEquivalence : Over U ≌ Opens ↥U where apply leOfHom V.hom))) counitIso := NatIso.ofComponents (fun V ↦ eqToIso (by aesop)) +variable {U} in +@[simp] lemma mem_overEquivalence_functor_obj {V : Over U} {x : U} : + x ∈ U.overEquivalence.functor.obj V ↔ x.1 ∈ V.left := .rfl + +section grothendieckTopology + +instance : U.overEquivalence.functor.IsDenseSubsite + ((Opens.grothendieckTopology X).over U) (Opens.grothendieckTopology U) where + functorPushforward_mem_iff {V S} := by + simp only [Opens.mem_grothendieckTopology, Sieve.mem_functorPushforward_functor] + constructor + · intro H x hxV + obtain ⟨W, f, hW, hxW⟩ := H ⟨x, V.hom.le hxV⟩ hxV + exact ⟨_, ((U.overEquivalence.symm.toAdjunction.homEquiv _ _ ).symm f).left, + ⟨_, _, 𝟙 _, hW, rfl⟩, _, hxW, rfl⟩ + · intro H x hxV + obtain ⟨W, f, ⟨W', hW'V, hWW', hSW'V, rfl⟩, hxW⟩ := H x hxV + exact ⟨_, U.overEquivalence.functor.map hW'V, + S.downward_closed hSW'V (U.overEquivalence.unitInv.app W'), hWW'.le hxW⟩ + +instance : U.overEquivalence.symm.inverse.IsDenseSubsite + ((Opens.grothendieckTopology X).over U) (Opens.grothendieckTopology U) := + inferInstanceAs (U.overEquivalence.functor.IsDenseSubsite ..) + +instance : U.overEquivalence.inverse.IsDenseSubsite + (Opens.grothendieckTopology U) ((Opens.grothendieckTopology X).over U) := + inferInstanceAs (U.overEquivalence.symm.functor.IsDenseSubsite ..) + +/-- Sheaves on the over category of `U` are equivalent to sheaves on `U` as a topological space. -/ +@[simps!] def sheafEquivOver : + Sheaf ((Opens.grothendieckTopology X).over U) A ≌ Sheaf (Opens.grothendieckTopology U) A := + U.overEquivalence.sheafCongr + ((Opens.grothendieckTopology X).over U) (Opens.grothendieckTopology U) A + +/-- `overPullback` and `sheafRestrict` are isomorphic under `sheafEquivOver`. -/ +def overPullbackSheafEquivOver {X : TopCat} (U : Opens X) : + (Opens.grothendieckTopology X).overPullback A U ⋙ U.sheafEquivOver.functor ≅ + U.sheafRestrict := .refl _ + +/-- `overPullback` and `sheafRestrict` are isomorphic under `sheafEquivOver`. -/ +def sheafRestrictSheafEquivOver {X : TopCat} (U : Opens X) : + U.sheafRestrict ⋙ U.sheafEquivOver.inverse ≅ + (Opens.grothendieckTopology X).overPullback A U := + U.overPullbackSheafEquivOver.isoCompInverse.symm + +end grothendieckTopology + end Opens end TopologicalSpace diff --git a/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean b/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean index 117ca1339f2113..392e27c8a3faf4 100644 --- a/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean +++ b/Mathlib/Topology/Sheaves/SheafCondition/Sites.lean @@ -174,6 +174,15 @@ theorem TopCat.Presheaf.isSheaf_of_isOpenEmbedding (h : IsOpenEmbedding f) (hF : have := h.functor_isContinuous exact Functor.op_comp_isSheaf _ _ _ ⟨_, hF⟩ +/-- The restriction functor of a sheaf to an open subspace. -/ +@[simps!] +def TopologicalSpace.Opens.sheafRestrict (U : Opens X) : + Sheaf (Opens.grothendieckTopology X) C ⥤ Sheaf (Opens.grothendieckTopology U) C := + haveI H : IsOpenEmbedding (TopCat.Hom.hom (TopCat.ofHom ⟨_, continuous_subtype_val⟩)) := + U.isOpenEmbedding + haveI := H.functor_isContinuous + H.isOpenMap.functor.sheafPushforwardContinuous C _ _ + variable (f) instance : RepresentablyFlat (Opens.map f) := by diff --git a/Mathlib/Topology/Sheaves/Stalks.lean b/Mathlib/Topology/Sheaves/Stalks.lean index 9403faaec5932e..b700bd02c76c02 100644 --- a/Mathlib/Topology/Sheaves/Stalks.lean +++ b/Mathlib/Topology/Sheaves/Stalks.lean @@ -117,6 +117,7 @@ lemma map_germ_eq_Γgerm (F : X.Presheaf C) {U : Opens X} {i : U ⟶ ⊤} (x : X variable {FC : C → C → Type*} {CC : C → Type*} [∀ X Y, FunLike (FC X Y) (CC X) (CC Y)] +@[simp] theorem germ_res_apply (F : X.Presheaf C) {U V : Opens X} (i : U ⟶ V) (x : X) (hx : x ∈ U) [ConcreteCategory C FC] (s) : F.germ U x hx (F.map i.op s) = F.germ V x (i.le hx) s := by diff --git a/Mathlib/Topology/Sion.lean b/Mathlib/Topology/Sion.lean index 9344fd6db31c1a..4b16ccbc13a746 100644 --- a/Mathlib/Topology/Sion.lean +++ b/Mathlib/Topology/Sion.lean @@ -143,7 +143,7 @@ theorem sublevelLeft_subset_union [AddCommGroup F] [Module ℝ F] The hypotheses imply that `sublevelLeft X f b z` is connected, and that the two other are disjoint. -/ - theorem sublevelLeft_subset_or [TopologicalSpace E] [AddCommGroup E] +theorem sublevelLeft_subset_or [TopologicalSpace E] [AddCommGroup E] [IsTopologicalAddGroup E] [Module ℝ E] [ContinuousSMul ℝ E] [AddCommGroup F] [Module ℝ F] (hfx' : ∀ x ∈ X, QuasiconcaveOn ℝ Y fun y => f x y) diff --git a/Mathlib/Topology/SmallInductiveDimension.lean b/Mathlib/Topology/SmallInductiveDimension.lean index 45e6ca64d319d1..c31d8e015a7647 100644 --- a/Mathlib/Topology/SmallInductiveDimension.lean +++ b/Mathlib/Topology/SmallInductiveDimension.lean @@ -48,21 +48,25 @@ class inductive HasSmallInductiveDimensionLT.{u} : (h : ∀ U ∈ s, HasSmallInductiveDimensionLT ↑(frontier U) n) : HasSmallInductiveDimensionLT X (n + 1) -variable (X : Type) [TopologicalSpace X] +variable {X : Type*} [TopologicalSpace X] +variable (X) in /-- A topological space has dimension `≤ n` if it has dimension `< n + 1`. -/ abbrev HasSmallInductiveDimensionLE (n : ℕ) := HasSmallInductiveDimensionLT X (n + 1) +variable (X) in /-- The small inductive dimension of a topological space. -/ noncomputable def smallInductiveDimension : WithBot ℕ∞ := sInf {n : WithBot ℕ∞ | ∀ (i : ℕ), n < i → HasSmallInductiveDimensionLT X i} -lemma HasSmallInductiveDimensionLT_zero_iff : - HasSmallInductiveDimensionLT X 0 ↔ IsEmpty X := +lemma hasSmallInductiveDimensionLT_zero_iff : HasSmallInductiveDimensionLT X 0 ↔ IsEmpty X := ⟨fun h ↦ by cases h; assumption, fun _ ↦ .zero⟩ -lemma HasSmallInductiveDimensionLT_one_iff : +@[deprecated (since := "2026-06-21")] +alias HasSmallInductiveDimensionLT_zero_iff := hasSmallInductiveDimensionLT_zero_iff + +lemma hasSmallInductiveDimensionLT_one_iff : HasSmallInductiveDimensionLT X 1 ↔ IsTopologicalBasis { s : Set X | IsClopen s } := by constructor · intro (.succ _ s hs h) @@ -72,4 +76,7 @@ lemma HasSmallInductiveDimensionLT_one_iff : rwa [isEmpty_coe_sort, (hs.isOpen hU).frontier_eq, sdiff_eq_empty] at ‹_› · exact fun h ↦ .succ 0 _ h fun _ hU ↦ hU.frontier_eq ▸ .zero +@[deprecated (since := "2026-06-21")] +alias HasSmallInductiveDimensionLT_one_iff := hasSmallInductiveDimensionLT_one_iff + end diff --git a/Mathlib/Topology/UniformSpace/Cauchy.lean b/Mathlib/Topology/UniformSpace/Cauchy.lean index 343f98439c2ac3..74fd21c99e4b5b 100644 --- a/Mathlib/Topology/UniformSpace/Cauchy.lean +++ b/Mathlib/Topology/UniformSpace/Cauchy.lean @@ -5,6 +5,7 @@ Authors: Johannes Hölzl, Mario Carneiro -/ module +public import Mathlib.Tactic.CrossRefAttribute public import Mathlib.Topology.Algebra.Constructions public import Mathlib.Topology.Bases public import Mathlib.Algebra.Order.Group.Nat @@ -365,6 +366,7 @@ theorem isComplete_iUnion_separated {ι : Sort*} {s : ι → Set α} (hs : ∀ i /-- A complete space is defined here using uniformities. A uniform space is complete if every Cauchy filter converges. -/ +@[wikidata Q848569] class CompleteSpace (α : Type u) [UniformSpace α] : Prop where /-- In a complete uniform space, every Cauchy filter converges. -/ complete : ∀ {f : Filter α}, Cauchy f → ∃ x, f ≤ 𝓝 x diff --git a/Mathlib/Topology/UniformSpace/Defs.lean b/Mathlib/Topology/UniformSpace/Defs.lean index f23cfccc9011c6..b2078d7073c3d9 100644 --- a/Mathlib/Topology/UniformSpace/Defs.lean +++ b/Mathlib/Topology/UniformSpace/Defs.lean @@ -189,6 +189,7 @@ theorem UniformSpace.Core.nhds_toTopologicalSpace {α : Type u} (u : Core α) (x A metric space has a natural uniformity, and a uniform space has a natural topology. A topological group also has a natural uniformity, even when it is not metrizable. -/ +@[wikidata Q652446] class UniformSpace (α : Type u) extends TopologicalSpace α where /-- The uniformity filter. -/ protected uniformity : Filter (α × α) diff --git a/Mathlib/Topology/UniformSpace/Dini.lean b/Mathlib/Topology/UniformSpace/Dini.lean index e5daead8c0747d..cd6fd6672de1b0 100644 --- a/Mathlib/Topology/UniformSpace/Dini.lean +++ b/Mathlib/Topology/UniformSpace/Dini.lean @@ -56,7 +56,7 @@ lemma tendstoLocallyUniformly_of_forall_tendsto refine (atTop : Filter ι).eq_or_neBot.elim (fun h ↦ ?eq_bot) (fun _ ↦ ?_) case eq_bot => simp [h, tendstoLocallyUniformly_iff_forall_tendsto] have F_le_f (x : α) (n : ι) : F n x ≤ f x := by - refine ge_of_tendsto (h_tendsto x) ?_ + refine _root_.ge_of_tendsto (h_tendsto x) ?_ filter_upwards [Ici_mem_atTop n] with m hnm exact hF_mono hnm x simp_rw [Metric.tendstoLocallyUniformly_iff, dist_eq_norm'] diff --git a/Mathlib/Topology/UniformSpace/ProdApproximation.lean b/Mathlib/Topology/UniformSpace/ProdApproximation.lean index 9975b76d1389d5..7ee1f6bc4a8a3c 100644 --- a/Mathlib/Topology/UniformSpace/ProdApproximation.lean +++ b/Mathlib/Topology/UniformSpace/ProdApproximation.lean @@ -95,9 +95,8 @@ lemma tensorHom_tmul (f : C(X, R)) (g : C(Y, R)) : tensorHom (f ⊗ₜ g) = prodMul f g := by rw [tensorHom, TensorProduct.lift.tmul] -lemma denseRange_tensorHom [CompactSpace X] [T2Space X] [CompactSpace Y] [T2Space Y] - [TotallyDisconnectedSpace X] : - DenseRange (tensorHom : C(X, R) ⊗[R] C(Y, R) → C(X × Y, R)) := by +lemma denseRange_tensorHom [CompactSpace X] [T2Space X] [CompactSpace Y] + [TotallyDisconnectedSpace X] : DenseRange (tensorHom : C(X, R) ⊗[R] C(Y, R) → C(X × Y, R)) := by let : UniformSpace R := IsTopologicalAddGroup.rightUniformSpace R let : IsUniformAddGroup R := isUniformAddGroup_of_addCommGroup intro f diff --git a/Mathlib/Topology/UniformSpace/Ultra/Completion.lean b/Mathlib/Topology/UniformSpace/Ultra/Completion.lean index 5aec77b1f95289..d06e74a7cea05e 100644 --- a/Mathlib/Topology/UniformSpace/Ultra/Completion.lean +++ b/Mathlib/Topology/UniformSpace/Ultra/Completion.lean @@ -36,26 +36,24 @@ instance CauchyFilter.isSymm_gen {s : SetRel X X} [s.IsSymm] : (gen s).IsSymm wh instance CauchyFilter.isTrans_gen {s : SetRel X X} [s.IsTrans] : (gen s).IsTrans where trans _ _ _ := IsTransitiveRel.mem_filter_prod_trans -set_option linter.flexible false in -- simp followed by infer_instance instance IsUltraUniformity.cauchyFilter [IsUltraUniformity X] : IsUltraUniformity (CauchyFilter X) := by apply mk_of_hasBasis (CauchyFilter.basis_uniformity IsUltraUniformity.hasBasis) - · exact fun _ ⟨_, hU, _⟩ ↦ by simp; infer_instance - · exact fun _ ⟨_, _, hU⟩ ↦ by simp; infer_instance + · exact fun _ ⟨_, hU, _⟩ ↦ by simpa using CauchyFilter.isSymm_gen + · exact fun _ ⟨_, _, hU⟩ ↦ by simpa using CauchyFilter.isTrans_gen @[simp] lemma IsUltraUniformity.cauchyFilter_iff : IsUltraUniformity (CauchyFilter X) ↔ IsUltraUniformity X := ⟨fun _ ↦ CauchyFilter.isUniformInducing_pureCauchy.isUltraUniformity, fun _ ↦ inferInstance⟩ -set_option linter.flexible false in -- simp followed by infer_instance instance IsUltraUniformity.separationQuotient [IsUltraUniformity X] : IsUltraUniformity (SeparationQuotient X) := by have := IsUltraUniformity.hasBasis.map (Prod.map SeparationQuotient.mk (SeparationQuotient.mk (X := X))) rw [← SeparationQuotient.uniformity_eq] at this apply mk_of_hasBasis this - · exact fun _ ⟨_, hU, _⟩ ↦ by simp; infer_instance + · exact fun _ ⟨_, hU, _⟩ ↦ by rw [id_eq]; infer_instance · rintro U ⟨hU', _, hU⟩ constructor rintro x y z diff --git a/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean b/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean index 04bc6ab33be7c0..452694a5ae1732 100644 --- a/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean +++ b/Mathlib/Topology/UniformSpace/Ultra/Constructions.lean @@ -77,15 +77,13 @@ instance IsUltraUniformity.pi {ι : Type*} {X : ι → Type*} [U : Π i, Uniform simpa +instances [Pi.uniformSpace_eq _] using this exact .iInf fun i ↦ .comap (h i) (Function.eval i) -set_option linter.flexible false in -- simp followed by infer_instance instance IsUltraUniformity.bot [UniformSpace X] [DiscreteUniformity X] : IsUltraUniformity X := by have := Filter.hasBasis_principal (SetRel.id (α := X)) rw [← DiscreteUniformity.eq_principal_setRelId] at this - apply mk_of_hasBasis this <;> { simp; infer_instance } + apply mk_of_hasBasis this <;> { rw [forall_const]; infer_instance } -set_option linter.flexible false in -- simp followed by infer_instance lemma IsUltraUniformity.top : @IsUltraUniformity X (⊤ : UniformSpace X) := by letI : UniformSpace X := ⊤ have := Filter.hasBasis_top (α := (X × X)) rw [← top_uniformity] at this - apply mk_of_hasBasis this <;> { simp; infer_instance } + apply mk_of_hasBasis this <;> { rw [forall_const]; infer_instance } diff --git a/Mathlib/Topology/VectorBundle/Basic.lean b/Mathlib/Topology/VectorBundle/Basic.lean index 21c11a5ea2ad57..ada696545834d0 100644 --- a/Mathlib/Topology/VectorBundle/Basic.lean +++ b/Mathlib/Topology/VectorBundle/Basic.lean @@ -86,15 +86,22 @@ theorem linear [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)] [∀ variable [AddCommMonoid F] [Module R F] [∀ x, AddCommMonoid (E x)] [∀ x, Module R (E x)] +open Classical in /-- A fiberwise linear inverse to `e`. -/ -@[simps!] protected def symmₗ (e : Pretrivialization F (π F E)) [e.IsLinear R] (b : B) : F →ₗ[R] E b := by - refine IsLinearMap.mk' (e.symm b) ?_ - by_cases hb : b ∈ e.baseSet - · exact (((e.linear R hb).mk' _).inverse (e.symm b) (e.symm_apply_apply_mk hb) fun v ↦ - congr_arg Prod.snd <| e.apply_mk_symm hb v).isLinear - · rw [e.coe_symm_of_notMem hb] - exact (0 : F →ₗ[R] E b).isLinear + refine if hb : b ∈ e.baseSet then IsLinearMap.mk' (e.symm b) ?_ else 0 + exact (((e.linear R hb).mk' _).inverse (e.symm b) (e.symm_apply_apply_mk hb) fun v ↦ + congr_arg Prod.snd <| e.apply_mk_symm hb v).isLinear + +@[simp] +lemma symmₗ_apply (e : Pretrivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) + (y : F) : e.symmₗ R b y = e.symm b y := by + simp [Pretrivialization.symmₗ, hb] + +@[simp] +lemma symmₗ_apply_of_notMem (e : Pretrivialization F (π F E)) [e.IsLinear R] {b : B} + (hb : b ∉ e.baseSet) (y : F) : e.symmₗ R b y = 0 := by + simp [Pretrivialization.symmₗ, hb] /-- A pretrivialization for a vector bundle defines linear equivalences between the fibers and the model space. -/ @@ -197,9 +204,19 @@ variable (R) in protected def symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] (b : B) : F →ₗ[R] E b := e.toPretrivialization.symmₗ R b -theorem coe_symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] (b : B) : - ⇑(e.symmₗ R b) = e.symm b := - rfl +theorem coe_symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) : + ⇑(e.symmₗ R b) = e.symm b := by + ext y; exact e.toPretrivialization.symmₗ_apply R hb y + +@[simp] +theorem symmₗ_apply (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) + (y : F) : e.symmₗ R b y = e.symm b y := + e.toPretrivialization.symmₗ_apply R hb y + +@[simp] +theorem symmₗ_apply_of_notMem (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} + (hb : b ∉ e.baseSet) (y : F) : e.symmₗ R b y = 0 := + e.toPretrivialization.symmₗ_apply_of_notMem R hb y variable (R) in /-- A fiberwise linear map equal to `e` on `e.baseSet`. -/ @@ -230,8 +247,8 @@ theorem linearMapAt_def_of_notMem (e : Trivialization F (π F E)) [e.IsLinear R] dif_neg hb theorem symm_linearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) - (y : E b) : e.symm b (e.linearMapAt R b y) = y := - e.toPretrivialization.symmₗ_linearMapAt hb y + (y : E b) : e.symm b (e.linearMapAt R b y) = y := by + simp [hb] theorem symmₗ_linearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) (y : E b) : e.symmₗ R b (e.linearMapAt R b y) = y := @@ -239,8 +256,8 @@ theorem symmₗ_linearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b : @[simp] theorem linearMapAt_symm (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) - (y : F) : e.linearMapAt R b (e.symm b y) = y := - e.toPretrivialization.linearMapAt_symmₗ hb y + (y : F) : e.linearMapAt R b (e.symm b y) = y := by + simp [hb] theorem linearMapAt_symmₗ (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) (y : F) : e.linearMapAt R b (e.symmₗ R b y) = y := @@ -395,19 +412,28 @@ lemma continuousLinearMapAt_apply_of_mem (e : Trivialization F TotalSpace.proj) simp [coe_linearMapAt_of_mem e hb] /-- Backwards map of `Bundle.Trivialization.continuousLinearEquivAt`, defined everywhere. -/ -@[simps -fullyApplied apply] def symmL (e : Trivialization F (π F E)) [e.IsLinear R] (b : B) : F →L[R] E b := { e.symmₗ R b with - toFun := e.symm b -- given explicitly to help `simps` cont := by by_cases hb : b ∈ e.baseSet · rw [(FiberBundle.totalSpaceMk_isInducing F E b).continuous_iff] + refine .congr (f := TotalSpace.mk b ∘ e.symm b) ?_ (by simp [hb]) exact e.continuousOn_symm.comp_continuous (.prodMk_right _) fun x ↦ mk_mem_prod hb (mem_univ x) - · refine continuous_zero.congr fun x => (e.symm_apply_of_notMem hb x).symm } + · exact continuous_zero.congr fun x => (e.symmₗ_apply_of_notMem hb x).symm } variable {R} +@[simp] +theorem symmL_apply (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) + (y : F) : e.symmL R b y = e.symm b y := + e.toPretrivialization.symmₗ_apply R hb y + +@[simp] +lemma symmL_apply_of_notMem (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} + (hb : b ∉ e.baseSet) (y : F) : e.symmL R b y = 0 := + e.toPretrivialization.symmₗ_apply_of_notMem _ hb _ + theorem symmL_continuousLinearMapAt (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) (y : E b) : e.symmL R b (e.continuousLinearMapAt R b y) = y := e.symmₗ_linearMapAt hb y @@ -427,7 +453,7 @@ def continuousLinearEquivAt (e : Trivialization F (π F E)) [e.IsLinear R] (b : invFun := e.symm b -- given explicitly to help `simps` continuous_toFun := (e.continuousOn.comp_continuous (FiberBundle.totalSpaceMk_isInducing F E b).continuous fun _ => e.mem_source.mpr hb).snd - continuous_invFun := (e.symmL R b).continuous } + continuous_invFun := by convert (e.symmL R b).continuous; ext; simp [hb] } theorem coe_continuousLinearEquivAt_eq (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} (hb : b ∈ e.baseSet) : @@ -440,12 +466,13 @@ theorem coe_continuousLinearEquivAt_eq' (e : Trivialization F (π F E)) [e.IsLin DFunLike.coe_injective (e.coe_linearMapAt_of_mem hb).symm theorem symm_continuousLinearEquivAt_eq (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} - (hb : b ∈ e.baseSet) : ((e.continuousLinearEquivAt R b hb).symm : F → E b) = e.symmL R b := - rfl + (hb : b ∈ e.baseSet) : ((e.continuousLinearEquivAt R b hb).symm : F → E b) = e.symmL R b := by + ext; simp [hb] theorem symm_continuousLinearEquivAt_eq' (e : Trivialization F (π F E)) [e.IsLinear R] {b : B} - (hb : b ∈ e.baseSet) : ((e.continuousLinearEquivAt R b hb).symm : F →L[R] E b) = e.symmL R b := - rfl + (hb : b ∈ e.baseSet) : + ((e.continuousLinearEquivAt R b hb).symm : F →L[R] E b) = e.symmL R b := by + ext; simp [hb] @[simp] theorem continuousLinearEquivAt_apply' (e : Trivialization F (π F E)) [e.IsLinear R] @@ -745,7 +772,7 @@ theorem trivializationAt_continuousLinearMapAt {b₀ b : B} theorem localTriv_symmL {b : B} (hb : b ∈ (Z.localTriv i).baseSet) : (Z.localTriv i).symmL R b = Z.coordChange i (Z.indexAt b) b := by ext1 v - rw [(Z.localTriv i).symmL_apply R, (Z.localTriv i).symm_apply] + rw [(Z.localTriv i).symmL_apply hb, (Z.localTriv i).symm_apply] exacts [rfl, hb] @[simp, mfld_simps] diff --git a/Mathlib/Topology/VectorBundle/Constructions.lean b/Mathlib/Topology/VectorBundle/Constructions.lean index 1ea7d3026627dc..6851c1994c7c01 100644 --- a/Mathlib/Topology/VectorBundle/Constructions.lean +++ b/Mathlib/Topology/VectorBundle/Constructions.lean @@ -74,7 +74,7 @@ instance vectorBundle : VectorBundle 𝕜 F (Bundle.Trivial B F) where @[simp] lemma symmₗ_trivialization (x : B) : (trivialization B F).symmₗ 𝕜 x = LinearMap.id := by - ext; simp [Trivialization.coe_symmₗ, trivialization_symm_apply B F] + ext; simp [trivialization_symm_apply B F] @[simp] lemma symmL_trivialization (x : B) : (trivialization B F).symmL 𝕜 x = ContinuousLinearMap.id 𝕜 F := by diff --git a/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean b/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean index 8a1fb98ff57e10..ffce633b4ed0d1 100644 --- a/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean +++ b/Mathlib/Topology/VectorBundle/ContinuousAlternatingMap.lean @@ -87,7 +87,7 @@ theorem inCoordinates_eq {x₀ x : B₁} {y₀ y : B₂} {ϕ : E₁ x [⋀^ι] |>.compContinuousAlternatingMap ϕ |>.compContinuousLinearMap (((trivializationAt F₁ E₁ x₀).continuousLinearEquivAt 𝕜 x hx).symm : F₁ →L[𝕜] E₁ x)) := by ext - simp [inCoordinates, *] + simp [inCoordinates, *, Function.comp_def] end ContinuousAlternatingMap @@ -277,7 +277,8 @@ def vectorPrebundle : (mem_baseSet_trivializationAt _ _ _) convert! (L₁.continuousAlternatingMapCongr L₂).toHomeomorph.isInducing ext f - simp [Trivialization.linearMapAt_def_of_mem _ (mem_baseSet_trivializationAt _ _ _), L₁, L₂] + simp [Trivialization.linearMapAt_def_of_mem _ (mem_baseSet_trivializationAt _ _ _), L₁, L₂, + Function.comp_def, mem_baseSet_trivializationAt] /-- Topology on the total space of the continuous `σ`-semilinear maps between two "normable" vector bundles over the same base. -/ diff --git a/Mathlib/Topology/VectorBundle/Hom.lean b/Mathlib/Topology/VectorBundle/Hom.lean index 2a2a8778a17f73..5162b27a435c26 100644 --- a/Mathlib/Topology/VectorBundle/Hom.lean +++ b/Mathlib/Topology/VectorBundle/Hom.lean @@ -154,10 +154,9 @@ theorem continuousLinearMapCoordChange_apply (b : B) simp_rw [continuousLinearMapCoordChange, ContinuousLinearEquiv.coe_coe, ContinuousLinearEquiv.arrowCongrSL_apply, continuousLinearMap_apply, continuousLinearMap_symm_apply' σ e₁ e₂ hb.1, comp_apply, ContinuousLinearEquiv.coe_coe, - ContinuousLinearEquiv.symm_symm, Trivialization.continuousLinearMapAt_apply, - Trivialization.symmL_apply] - rw [e₂.coordChangeL_apply e₂', e₁'.coordChangeL_apply e₁, e₁.coe_linearMapAt_of_mem hb.1.1, - e₂'.coe_linearMapAt_of_mem hb.2.2] + ContinuousLinearEquiv.symm_symm, Trivialization.continuousLinearMapAt_apply] + rw [e₂.symmL_apply hb.1.2, e₁'.symmL_apply hb.2.1, e₂.coordChangeL_apply e₂', + e₁'.coordChangeL_apply e₁, e₁.coe_linearMapAt_of_mem hb.1.1, e₂'.coe_linearMapAt_of_mem hb.2.2] exacts [⟨hb.2.1, hb.1.1⟩, ⟨hb.1.2, hb.2.2⟩] end Bundle.Pretrivialization @@ -208,7 +207,8 @@ def Bundle.ContinuousLinearMap.vectorPrebundle : convert! this ext f dsimp [Pretrivialization.continuousLinearMap_apply] - rw [Trivialization.linearMapAt_def_of_mem _ (mem_baseSet_trivializationAt _ _ _)] + simp only [Trivialization.symmL_apply, mem_baseSet_trivializationAt, + Trivialization.linearMapAt_def_of_mem] rfl /-- Topology on the total space of the continuous `σ`-semilinear maps between two "normable" vector @@ -520,7 +520,7 @@ theorem inCoordinates_apply_eq₂ (trivializationAt F₃ E₃ x₀).linearMapAt 𝕜 x (ϕ ((trivializationAt F₁ E₁ x₀).symm x v) ((trivializationAt F₂ E₂ x₀).symm x w)) := by rw [inCoordinates_eq h₁x (by simp [h₂x, h₃x])] - simp [hom_trivializationAt, Trivialization.continuousLinearMap_apply] + simp [hom_trivializationAt, Trivialization.continuousLinearMap_apply, h₂x] end TwoVariables diff --git a/Mathlib/Topology/VectorBundle/Riemannian.lean b/Mathlib/Topology/VectorBundle/Riemannian.lean index cf108f20289169..a5ae4263dd779a 100644 --- a/Mathlib/Topology/VectorBundle/Riemannian.lean +++ b/Mathlib/Topology/VectorBundle/Riemannian.lean @@ -183,7 +183,8 @@ lemma eventually_norm_symmL_trivializationAt_self_comp_lt (x : B) {r : ℝ} (hr let w := (trivializationAt F E x).continuousLinearMapAt ℝ y v suffices ‖((trivializationAt F E x).symmL ℝ x) w‖ ^ 2 ≤ r' ^ 2 * ‖v‖ ^ 2 from le_of_sq_le_sq (by simpa [mul_pow]) (by positivity) - simp only [Trivialization.symmL_apply, ← real_inner_self_eq_norm_sq, hg] + simp only [Trivialization.symmL_apply, mem_baseSet_trivializationAt, + ← real_inner_self_eq_norm_sq, hg] have hgy : g y v v = g' y w w := by rw [inCoordinates_apply_eq₂ h'y h'y (Set.mem_univ _)] have A : ((trivializationAt F E x).symm y) @@ -228,8 +229,8 @@ lemma eventually_norm_trivializationAt_lt (x : B) : ((trivializationAt F E x).symmL ℝ x) = ContinuousLinearMap.id _ _ := by ext v have h'x : x ∈ (trivializationAt F E x).baseSet := FiberBundle.mem_baseSet_trivializationAt' x - simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply, comp_apply, - id_apply] + simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply, + mem_baseSet_trivializationAt, comp_apply, id_apply] convert! ((trivializationAt F E x).continuousLinearEquivAt ℝ _ h'x).apply_symm_apply v simp [Trivialization.coe_continuousLinearEquivAt_eq _ h'x] have : (trivializationAt F E x).continuousLinearMapAt ℝ y = @@ -286,7 +287,7 @@ lemma eventually_norm_symmL_trivializationAt_comp_self_lt (x : B) {r : ℝ} (hr let w := (trivializationAt F E x).continuousLinearMapAt ℝ x v suffices ‖((trivializationAt F E x).symmL ℝ y) w‖ ^ 2 ≤ r' ^ 2 * ‖v‖ ^ 2 from le_of_sq_le_sq (by simpa [mul_pow]) (by positivity) - simp only [Trivialization.symmL_apply, ← real_inner_self_eq_norm_sq, hg] + simp only [Trivialization.symmL_apply, h'y, ← real_inner_self_eq_norm_sq, hg] have hgx : g x v v = g' x w w := by rw [inCoordinates_apply_eq₂ h'x h'x (Set.mem_univ _)] have A : ((trivializationAt F E x).symm x) @@ -333,8 +334,8 @@ lemma eventually_norm_symmL_trivializationAt_lt (x : B) : ((trivializationAt F E x).symmL ℝ x) = ContinuousLinearMap.id _ _ := by ext v have h'x : x ∈ (trivializationAt F E x).baseSet := FiberBundle.mem_baseSet_trivializationAt' x - simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply, comp_apply, - id_apply] + simp only [Trivialization.continuousLinearMapAt_apply, Trivialization.symmL_apply, + mem_baseSet_trivializationAt, comp_apply, id_apply] convert! ((trivializationAt F E x).continuousLinearEquivAt ℝ _ h'x).apply_symm_apply v simp [Trivialization.coe_continuousLinearEquivAt_eq _ h'x] have : (trivializationAt F E x).symmL ℝ y = diff --git a/Mathlib/Util/CountHeartbeats.lean b/Mathlib/Util/CountHeartbeats.lean index 4180ad42d159ef..72c4fe8b668c19 100644 --- a/Mathlib/Util/CountHeartbeats.lean +++ b/Mathlib/Util/CountHeartbeats.lean @@ -153,7 +153,7 @@ elab "#count_heartbeats " approx:(&"approximately ")? "in" ppLine cmd:command : let m : TSyntax `num := quote max' Command.liftCoreM <| MetaM.run' do Lean.Meta.Tactic.TryThis.addSuggestion (← getRef) - (← set_option hygiene false in `(command| set_option maxHeartbeats $m in $cmd)) + (← (set_option hygiene false in `(command| set_option maxHeartbeats $m in $cmd))) set_option linter.style.maxHeartbeats false in /-- diff --git a/Mathlib/Util/GetAllModules.lean b/Mathlib/Util/GetAllModules.lean index 5da0463be9851c..09461a0d52f3ab 100644 --- a/Mathlib/Util/GetAllModules.lean +++ b/Mathlib/Util/GetAllModules.lean @@ -54,5 +54,5 @@ In addition, these names are sorted in a platform-independent order. -/ def getAllModulesSorted (git : Bool) (ml : String) : IO (Array String) := do let files ← getAllFiles git ml let names ← files.mapM fun f => do - return (← moduleNameOfFileName f none).toString + return (← moduleNameOfFileName f none).toString return names.qsort (· < ·) diff --git a/Mathlib/Util/WhatsNew.lean b/Mathlib/Util/WhatsNew.lean index a14267afd8b85e..b833d1f05b79f0 100644 --- a/Mathlib/Util/WhatsNew.lean +++ b/Mathlib/Util/WhatsNew.lean @@ -108,7 +108,7 @@ def whatsNew (old new : Environment) : CoreM MessageData := do diffs := diffs.push (← printIdCore c i) for ext in ← persistentEnvExtensionsRef.get do - if let some diff := ← diffExtension old new ext then + if let some diff ← diffExtension old new ext then diffs := diffs.push diff if diffs.isEmpty then return "no new constants" diff --git a/MathlibTest/Attribute/ToAdditive/Basic.lean b/MathlibTest/Attribute/ToAdditive/Basic.lean index 9496d27dc41f71..4e815db07b96e8 100644 --- a/MathlibTest/Attribute/ToAdditive/Basic.lean +++ b/MathlibTest/Attribute/ToAdditive/Basic.lean @@ -96,17 +96,19 @@ class my_has_scalar (M : Type u) (α : Type v) where instance : my_has_scalar Nat Nat := ⟨fun a b => a * b⟩ attribute [to_additive (reorder := α β) my_has_scalar] my_has_pow +set_option pp.mvars.anonymous false in /-- error: `to_additive` validation failed: expected - {α : Type u} → {β : Type v} → [self : my_has_scalar β α] → α → β → α + {α : Type _} → {β : Type _} → [self : my_has_scalar β α] → α → β → α but 'Test.my_has_scalar.smul' has type {M : Type u} → {α : Type v} → [self : my_has_scalar M α] → M → α → α -/ #guard_msgs in attribute [to_additive existing] my_has_pow.pow +set_option pp.mvars.anonymous false in /-- error: `to_additive` validation failed: expected - {β : Type u} → {α : Type v} → [self : my_has_scalar β α] → α → β → α + {β : Type _} → {α : Type _} → [self : my_has_scalar β α] → α → β → α but 'Test.my_has_scalar.smul' has type {M : Type u} → {α : Type v} → [self : my_has_scalar M α] → M → α → α -/ @@ -142,6 +144,7 @@ def foo4 {α : Type u} : Type v → Type (max u v) := @my_has_pow α @[to_additive bar4_test] lemma foo4_test {α β : Type u} : @foo4 α β = @my_has_pow α β := rfl +set_option linter.defProp false in @[to_additive bar5] def foo5 {α} [my_has_pow α ℕ] [my_has_pow ℕ ℤ] : True := True.intro @@ -304,6 +307,7 @@ attribute [to_additive add_some_def] some_def run_cmd do liftCoreM <| successIfFail (getConstInfo `Test.add_some_def.in_namespace) +set_option linter.defProp false in set_option linter.unusedVariables false in def foo_mul {I J K : Type} (n : ℕ) {f : I → Type} (L : Type) [∀ i, One (f i)] [Add I] [Mul L] : true := by trivial @@ -581,7 +585,7 @@ lemma one_eq_one'' {α : Type*} [One α] : (1 : α) = 1 := rfl /-- error: `to_additive` validation failed: expected - ∀ {α : Type u} [inst : Zero α], 0 = 0 + ∀ {α : Type ?u.1} [inst : Zero α], 0 = 0 but 'Eq.trans' has type ∀ {α : Sort u} {a b c : α}, a = b → b = c → a = c -/ @@ -911,6 +915,7 @@ def monoidAlgebraFoo₂ {k G : Type} [Inhabited k] : MonoidAlgebra k G × Nat := (⟨fun _ ↦ default⟩, 2) -- Proofs in types aren't abstracted: +set_option linter.defProp false in @[to_additive] def abstractMul : Function.const _ True (id Nat.zero_lt_one) := trivial diff --git a/MathlibTest/Attribute/ToDual.lean b/MathlibTest/Attribute/ToDual.lean index 890c9c996640e3..3eec586c7882c7 100644 --- a/MathlibTest/Attribute/ToDual.lean +++ b/MathlibTest/Attribute/ToDual.lean @@ -55,22 +55,24 @@ def SemilatticeSup.casesOn' {α} {motive : SemilatticeSup α → Sort*} (t : Sem t.casesOn mk /-- -info: SemilatticeInf.casesOn'.{u_1} {α : Type} {motive : SemilatticeInf α → Sort u_1} - (mk : - [inst : Min α] → - [inst_1 : PartialOrder α] → - (sup_le : ∀ (b a c : α), c ≤ a → c ≤ b → c ≤ a ⊓ b) → - motive { toPartialOrder := inst_1, toMin := inst, le_inf := ⋯ }) - (t : SemilatticeInf α) : motive t +info: @[expose] def SemilatticeInf.casesOn'.{u_1} : {α : Type} → + {motive : SemilatticeInf α → Sort u_1} → + ([inst : Min α] → + [inst_1 : PartialOrder α] → + (sup_le : ∀ (b a c : α), c ≤ a → c ≤ b → c ≤ a ⊓ b) → + motive { toPartialOrder := inst_1, toMin := inst, le_inf := ⋯ }) → + (t : SemilatticeInf α) → motive t := +fun {α} {motive} mk t => SemilatticeInf.casesOn t fun [PartialOrder α] [Min α] sup_le => mk ⋯ -/ #guard_msgs in -#check SemilatticeInf.casesOn' +#print SemilatticeInf.casesOn' class Semilattice (α : Type) extends SemilatticeInf α, SemilatticeSup α attribute [to_dual existing] Semilattice.toSemilatticeSup -- when reordering arguments in arguments that are being reordered, -- there is a convenient syntax to specify this at the same time: +set_option linter.defProp false in @[to_dual self (reorder := h₁ h₂ (a b))] def SemilatticeSup.foo {α} [Semilattice α] (h₁ : ∀ a b : α, a ⊔ b ≤ b) (h₂ : ∀ a b : α, a ≤ b ⊓ a) (my_sorry : ∀ {p : Prop}, p) : False := @@ -310,6 +312,7 @@ info: theorem Cov.Ioc_def : ∀ {α : Type} [inst : PartialOrder α] {a b x : α /-! Test that translated autoparams are marked with `meta`. -/ +set_option linter.defProp false in @[to_dual] def Top.autoParamTest {a b : α} (h : a ≤ b := by grind) : a ≤ b := h @@ -362,6 +365,7 @@ private theorem WithBotPrivate.coe_le_top : WithTop.coe a ≤ .top := .le_top (W run_meta guard <| (← getEnv).contains ``WithTopPrivate.coe_le_bot +set_option linter.defProp false in set_option linter.unusedVariables false in @[to_dual (rename := x → y, Pbot ↔ Ptop) renameTest'] def renameTest [Top α] [Bot α] (x : α) {P : α → Prop} (Ptop : P ⊤) (Pbot : P ⊥) : True := trivial @@ -388,37 +392,32 @@ info: eq_of_max_of_min {α : Type} [PartialOrder α] (a b : α) (hmin : ∀ (x : theorem le_of_lt_and_le_of_lt {β} [Preorder β] (a b : α) (c d : β) : (a < b → a ≤ b) ∧ (c < d → c ≤ d) := ⟨le_of_lt, (fun γ [Preorder γ] (c d : γ) ↦ @le_of_lt γ _ c d) β c d⟩ --- Test the reordering of universes -@[to_dual (reorder := α β γ) universeTest1'] -def universeTest1.{u,v,w} (α : Type u) (β : Type v) (γ : Type w) := α × β × γ +/-! Test the reordering of universes -/ -/-- info: universeTest1'.{w, u, v} (γ : Type w) (α : Type u) (β : Type v) : Type (max u w v) -/ -#guard_msgs in -#check universeTest1' +def universeTest1.{u,v,w} (α : Type u) (β : Type v) (γ : Type w) := α × β × γ +@[to_dual existing (reorder := α β γ) universeTest1] +def universeTest1'.{v,w,u} (α : Type u) (β : Type v) (γ : Type w) := α × β × γ +@[to_dual none] alias universeTest1'' := universeTest1 @[to_dual (reorder := u₁ u₂) universeTest2'] def universeTest2.{u,v} (u₁ : PUnit.{u}) (u₂ : PUnit.{v}) := PProd.mk u₁ u₂ -/-- info: universeTest2'.{v, u} (u₂ : PUnit) (u₁ : PUnit) : PUnit ×' PUnit -/ +/-- info: universeTest2'.{u, v} (u₂ : PUnit) (u₁ : PUnit) : PUnit ×' PUnit -/ #guard_msgs in #check universeTest2' -@[to_dual (reorder := u₁ u₂) universeTest3'] -def universeTest3.{u,u',v,v'} (u₁ : PProd PUnit.{u} PUnit.{u'}) (u₂ : PProd PUnit.{v} PUnit.{v'}) := - PProd.mk u₁ u₂ - -/-- -info: universeTest3'.{v, v', u, u'} (u₂ : PUnit ×' PUnit) (u₁ : PUnit ×' PUnit) : (PUnit ×' PUnit) ×' PUnit ×' PUnit --/ -#guard_msgs in -#check universeTest3' - class Category.{v,u} (c : Type u) where bla : Type v @[to_dual self (reorder := A B, 2 4)] structure Comma {A : Type u} [Category.{v} A] {B : Type u'} [Category.{v'} B] where +@[to_dual self (reorder := α β, 3 4)] +axiom HLE {α β : Type*} : α → β → Prop + +@[to_dual self (reorder := α γ, a c, 7 8)] +axiom hle_trans {α β γ : Type*} (a : α) (b : β) (c : γ) : HLE a b → HLE b c → HLE a c + open Mathlib.Tactic Translate ToDual /-- info: "leftMono" -/ diff --git a/MathlibTest/CategoryTheory/CategoryStar.lean b/MathlibTest/CategoryTheory/CategoryStar.lean index c5f5ccbf709e1d..5e0f143a13f2d0 100644 --- a/MathlibTest/CategoryTheory/CategoryStar.lean +++ b/MathlibTest/CategoryTheory/CategoryStar.lean @@ -66,7 +66,9 @@ universe u₁ u₂ variable (E : Sort (imax (u₁ + 1) (u₂ + 1))) [Category* E] variable (F : Sort (max (u₁ + 1) (u₂ + 1))) [Category* F] +set_option linter.checkUnivs false in def barE := E ⥤ E +set_option linter.checkUnivs false in def barF := F ⥤ F /-- diff --git a/MathlibTest/ClickSuggestions/Test.lean b/MathlibTest/ClickSuggestions/Test.lean index 4bac764b6ddc0d..884700e3f7b4ae 100644 --- a/MathlibTest/ClickSuggestions/Test.lean +++ b/MathlibTest/ClickSuggestions/Test.lean @@ -184,3 +184,10 @@ example (a b c : Nat) : a + b + c = a + b := by click_test "/1" => "nth_rw 2 [Nat.add_comm a b]" click_test "/0/1/0/1" => "nth_rw 1 [Nat.add_comm a b]" exact test_sorry + +-- This example used to panic +example : True := by + by_cases h : False + · click_test h "" => "rw [← true_eq_false_of_false h] at h" + trivial + · trivial diff --git a/MathlibTest/DifferentialGeometry/Notation/Advanced.lean b/MathlibTest/DifferentialGeometry/Notation/Advanced.lean index 2355bf61ff17c2..1f0384072fa830 100644 --- a/MathlibTest/DifferentialGeometry/Notation/Advanced.lean +++ b/MathlibTest/DifferentialGeometry/Notation/Advanced.lean @@ -195,9 +195,7 @@ section interaction -- Note: these tests might be incomplete; extend as needed! -/-- -info: MDifferentiableAt I (I.prod (modelWithCornersSelf 𝕜 E)) fun m ↦ TotalSpace.mk' E m (X m) : M → Prop --/ +/-- info: MDifferentiableAt I I.tangent fun m ↦ TotalSpace.mk' E m (X m) : M → Prop -/ #guard_msgs in #check MDiffAt (T% X) @@ -234,13 +232,10 @@ Hint: Additional diagnostic information may be available using the `set_option d --- trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `TotalSpace F (TangentSpace I)` [Elab.DiffGeo.MDiff] ✅️ TotalSpace - [Elab.DiffGeo.MDiff] 💥️ From base info - [Elab.DiffGeo.MDiff] Failed with error: - No `baseInfo` provided [Elab.DiffGeo.MDiff] ✅️ TangentSpace [Elab.DiffGeo.MDiff] `TangentSpace I` is the total space of the `TangentBundle` of `M` - [Elab.DiffGeo.MDiff] Found model: `I.prod I.tangent` - [Elab.DiffGeo.MDiff] Found model: `I.prod I.tangent` + [Elab.DiffGeo.MDiff] Found model: `I.tangent` + [Elab.DiffGeo.MDiff] Found model: `I.tangent` [Elab.DiffGeo.MDiff] Finding a model with corners for: `F` [Elab.DiffGeo.MDiff] 💥️ TotalSpace [Elab.DiffGeo.MDiff] Failed with error: @@ -383,10 +378,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `M` `ContinuousLinearMap id' E'' E'''` is not a coercion of a set to a type [Elab.DiffGeo.MDiff] 💥️ NormedField [Elab.DiffGeo.MDiff] Failed with error: - failed to synthesize instance of type class + failed to synthesize NontriviallyNormedField (ContinuousLinearMap id' E'' E''') ⏎ - Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command. + Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command. [Elab.DiffGeo.MDiff] 💥️ InnerProductSpace [Elab.DiffGeo.MDiff] Failed with error: Couldn't find an `InnerProductSpace` structure on `ContinuousLinearMap id' E'' E'''` among local instances. @@ -486,10 +481,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `M` `ContinuousLinearMap σ E'' E''''` is not a coercion of a set to a type [Elab.DiffGeo.MDiff] 💥️ NormedField [Elab.DiffGeo.MDiff] Failed with error: - failed to synthesize instance of type class + failed to synthesize NontriviallyNormedField (ContinuousLinearMap σ E'' E'''') ⏎ - Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command. + Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command. [Elab.DiffGeo.MDiff] 💥️ InnerProductSpace [Elab.DiffGeo.MDiff] Failed with error: Couldn't find an `InnerProductSpace` structure on `ContinuousLinearMap σ E'' E''''` among local instances. @@ -691,10 +686,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `↑(Set.Icc x y)` `Set.Icc x y` is not a sphere in a real normed space [Elab.DiffGeo.MDiff] 💥️ NormedField [Elab.DiffGeo.MDiff] Failed with error: - failed to synthesize instance of type class + failed to synthesize NontriviallyNormedField ↑(Set.Icc x y) ⏎ - Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command. + Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command. [Elab.DiffGeo.MDiff] 💥️ InnerProductSpace [Elab.DiffGeo.MDiff] Failed with error: Couldn't find an `InnerProductSpace` structure on `↑(Set.Icc x y)` among local instances. @@ -956,9 +951,8 @@ variable {σ : Π x : M, V x} {σ' : (x : E) → Trivial E E' x} {s : E → E'} variable (X : (m : M) → TangentSpace I m) [IsManifold I 1 M] {x : M} /-- -info: mfderiv I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m)) - x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x) - (TangentSpace (I.prod (modelWithCornersSelf 𝕜 E)) (TotalSpace.mk' E x (X x))) +info: mfderiv I I.tangent (fun m ↦ TotalSpace.mk' E m (X m)) + x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x) (TangentSpace I.tangent (TotalSpace.mk' E x (X x))) -/ #guard_msgs in #check mfderiv% (T% X) x @@ -1099,24 +1093,19 @@ variable {σ : Π x : M, V x} {σ' : (x : E) → Trivial E E' x} {s : E → E'} variable (X : (m : M) → TangentSpace I m) [IsManifold I 1 M] {x : M} /-- -info: mfderiv I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m)) - x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x) - (TangentSpace (I.prod (modelWithCornersSelf 𝕜 E)) (TotalSpace.mk' E x (X x))) +info: mfderiv I I.tangent (fun m ↦ TotalSpace.mk' E m (X m)) + x : ContinuousLinearMap (RingHom.id 𝕜) (TangentSpace I x) (TangentSpace I.tangent (TotalSpace.mk' E x (X x))) -/ #guard_msgs in #check mfderiv% (T% X) x variable {dXm : TangentSpace I x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, E)) (TotalSpace.mk' E x (X x))} -/-- -info: HasMFDerivAt I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m)) x dXm : Prop --/ +/-- info: HasMFDerivAt I I.tangent (fun m ↦ TotalSpace.mk' E m (X m)) x dXm : Prop -/ #guard_msgs in #check HasMFDerivAt% (T% X) x dXm -/-- -info: HasMFDerivWithinAt I (I.prod (modelWithCornersSelf 𝕜 E)) (fun m ↦ TotalSpace.mk' E m (X m)) t x dXm : Prop --/ +/-- info: HasMFDerivWithinAt I I.tangent (fun m ↦ TotalSpace.mk' E m (X m)) t x dXm : Prop -/ #guard_msgs in variable {t : Set M} in #check HasMFDerivAt[t] (T% X) x dXm diff --git a/MathlibTest/DifferentialGeometry/Notation/Basic.lean b/MathlibTest/DifferentialGeometry/Notation/Basic.lean index 862e96f248d2da..055a66b9f12458 100644 --- a/MathlibTest/DifferentialGeometry/Notation/Basic.lean +++ b/MathlibTest/DifferentialGeometry/Notation/Basic.lean @@ -529,6 +529,71 @@ end end differentiability +/-! Tests for the elaborators for `UniqueMDiff{WithinAt,On}`. -/ +section UniqueMDiff + +variable {s : Set M} {m : M} + +/-- info: UniqueMDiffOn I s : Prop -/ +#guard_msgs in +#check UniqueMDiff[s] + +/-- info: UniqueMDiffOn (modelWithCornersSelf Real Real) (Set.Icc 0 1) : Prop -/ +#guard_msgs in +#check UniqueMDiff[(Set.Icc 0 1 : Set ℝ)] + +/-- error: `Real` has type `Type` which is not of the form `Set α` for some `α`. -/ +#guard_msgs in +#check UniqueMDiff[ℝ] + +/-- info: UniqueMDiffWithinAt I s : M → Prop -/ +#guard_msgs in +#check UniqueMDiffAt[s] + +/-- info: UniqueMDiffWithinAt I s m : Prop -/ +#guard_msgs in +#check UniqueMDiffAt[s] m + +/-- info: UniqueMDiffWithinAt I Set.univ m : Prop -/ +#guard_msgs in +#check UniqueMDiffAt[(Set.univ : Set M)] m + +-- In the future, the elaborators should take the type of `m` into account. +/-- +error: Could not find a model with corners for `?_`. + +Hint: the expected type contains metavariables, maybe you need to provide an implicit argument +-/ +#guard_msgs in +set_option pp.mvars.anonymous false in +#check UniqueMDiffAt[Set.univ] m + +variable {s : TopologicalSpace.Opens M} + +/-- info: UniqueMDiffOn I s.carrier : Prop -/ +#guard_msgs in +#check UniqueMDiff[s.carrier] + +/-- error: `s` has type `TopologicalSpace.Opens M` which is not of the form `Set α` for some `α`. -/ +#guard_msgs in +#check UniqueMDiff[s] + +/-- +error: Application type mismatch: The argument + s +has type + TopologicalSpace.Opens M +but is expected to have type + Set ?_ +in the application + UniqueMDiffOn I s +-/ +#guard_msgs in +set_option pp.mvars.anonymous false in +#check UniqueMDiffOn I s + +end UniqueMDiff + /-! Tests for the custom elaborators for `ContMDiff{WithinAt,At,On}` -/ section smoothness @@ -970,6 +1035,43 @@ open ContDiff in -- for the ∞ notation end +/-! Inferring a model with corners on a normed space, for an `IsManifold` hypothesis -/ +section + +open scoped ContDiff + +variable {X Y : Type*} [TopologicalSpace X] [ChartedSpace ℝ X] [IsManifold 𝓘(ℝ) ω X] + [TopologicalSpace Y] [ChartedSpace ℝ Y] [IsManifold 𝓘(ℝ) ω Y] {f : X → Y} + +/-- +info: ContMDiff (modelWithCornersSelf Real Real) (modelWithCornersSelf Real Real) Top.top f : Prop +-/ +#guard_msgs in +#check CMDiff ω f + +variable {f : X → ℝ} in /-- +info: MDifferentiable (modelWithCornersSelf Real Real) (modelWithCornersSelf Real Real) f : Prop +-/ +#guard_msgs in #check MDiff f + +variable {X : Type*} [TopologicalSpace X] [ChartedSpace F X] [IsManifold 𝓘(𝕜, F) ω X] {f : X → 𝕜} in +/-- info: MDifferentiable (modelWithCornersSelf 𝕜 F) (modelWithCornersSelf 𝕜 𝕜) f : Prop -/ +#guard_msgs in +#check MDiff f + +-- This test is expected to fail: it passing would amount to guessing a model with corners on +-- a product of two normed spaces (which is ambiguous). +variable {X : Type*} [TopologicalSpace X] [ChartedSpace (F × F) X] [IsManifold 𝓘(𝕜, F × F) ω X] {f : X → 𝕜} in +/-- +error: Could not find a model with corners for `X`. + +Hint: failures to find a model with corners can be debugged with the command `set_option trace.Elab.DiffGeo.MDiff true`. +-/ +#guard_msgs in +#check MDiff f + +end + /-! Tests for the elaborators for `tangentMap(Within)` and `TangentSpace` -/ section @@ -1368,10 +1470,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `Unit` `Unit` is not a coercion of a set to a type [Elab.DiffGeo.MDiff] 💥️ NormedField [Elab.DiffGeo.MDiff] Failed with error: - failed to synthesize instance of type class + failed to synthesize NontriviallyNormedField Unit ⏎ - Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command. + Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command. [Elab.DiffGeo.MDiff] 💥️ InnerProductSpace [Elab.DiffGeo.MDiff] Failed with error: Couldn't find an `InnerProductSpace` structure on `Unit` among local instances. diff --git a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean index ce05e61ed64c58..6c43114e28e058 100644 --- a/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean +++ b/MathlibTest/DifferentialGeometry/Notation/Delaborators.lean @@ -18,7 +18,7 @@ variable {E : Type*} [NormedAddCommGroup E] [NormedSpace ℝ E] {H : Type*} [TopologicalSpace H] {I : ModelWithCorners ℝ E H} {M : Type*} [TopologicalSpace M] [ChartedSpace H M] [IsManifold I ∞ M] - (f : M → M) (x : M) (s : Set M) + (f : M → M) (x : M) (s : Set M) (f' : TangentSpace I x →L[ℝ] TangentSpace I (f x)) (v : (x : M) → TangentSpace I x) /-- info: MDiff f : Prop -/ @@ -70,10 +70,18 @@ variable #guard_msgs in #check mfderiv% f x -/-- info: mfderiv% (T% v) x : TangentSpace I x →L[ℝ] TangentSpace (I.prod 𝓘(ℝ, E)) ⟨x, v x⟩ -/ +/-- info: mfderiv% (T% v) x : TangentSpace I x →L[ℝ] TangentSpace I.tangent ⟨x, v x⟩ -/ #guard_msgs in #check mfderiv% (T% v) x +/-- info: mfderiv[s] f x : TangentSpace I x →L[ℝ] TangentSpace I (f x) -/ +#guard_msgs in +#check mfderivWithin I I f s x + +/-- info: mfderiv[s] f x : TangentSpace I x →L[ℝ] TangentSpace I (f x) -/ +#guard_msgs in +#check mfderiv[s] f x + /-- info: ⟨x, v x⟩ : TotalSpace E (TangentSpace I) -/ #guard_msgs in #check TotalSpace.mk' E x (v x) @@ -82,6 +90,127 @@ variable #guard_msgs in #check TotalSpace.mk (F := E) x (v x) +/-- info: UniqueMDiff[s] : Prop -/ +#guard_msgs in +#check UniqueMDiffOn I s + +/-- info: UniqueMDiffAt[s] : M → Prop -/ +#guard_msgs in +#check UniqueMDiffWithinAt I s + +/-- info: UniqueMDiffAt[s] x : Prop -/ +#guard_msgs in +#check UniqueMDiffWithinAt I s x + +/-- info: UniqueMDiffAt[s] : M → Prop -/ +#guard_msgs in +#check UniqueMDiffWithinAt (𝕜 := ℝ) I s + +/-- info: HasMFDerivAt[s] f x f' : Prop -/ +#guard_msgs in +#check HasMFDerivWithinAt I I f s x f' + +/-- info: HasMFDerivAt[s] f x f' : Prop -/ +#guard_msgs in +#check HasMFDerivAt[s] f x f' + +/-- info: HasMFDerivAt% f x f' : Prop -/ +#guard_msgs in +#check HasMFDerivAt I I f x f' + +/-- info: HasMFDerivAt% f x f' : Prop -/ +#guard_msgs in +#check HasMFDerivAt% f x f' + +section TotalSpace + +variable {𝕜 B : Type*} {E : B → Type*} + +variable + -- Let `E` be a fiber bundle with base `B` and fiber `F` (a vector space over `𝕜`) + [TopologicalSpace B] [TopologicalSpace (TotalSpace F E)] [∀ x, TopologicalSpace (E x)] + [NormedAddCommGroup F] [NontriviallyNormedField 𝕜] [NormedSpace 𝕜 F] [FiberBundle F E] + -- Moreover let `E` be a vector bundle + [(x : B) → AddCommGroup (E x)] [(x : B) → Module 𝕜 (E x)] [VectorBundle 𝕜 F E] + -- Let the base `B` be charted over a fixed model space `HB` + {HB : Type*} [TopologicalSpace HB] [ChartedSpace HB B] + -- Moreover let `HB` be modelled on a normed space `EB` so that `B` (and hence `E`) have + -- differentiable structures + {EB : Type*} [NormedAddCommGroup EB] [NormedSpace 𝕜 EB] {I : ModelWithCorners 𝕜 EB HB} + +variable {f : B → 𝕜} {a : 𝕜} {s : Π x : B, E x} {u : Set B} {x₀ : B} + {f' : TangentSpace I x₀ →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) (⟨x₀, s x₀⟩ : TotalSpace F E)} + +/-- +info: mfderiv% (T% s) : (x : B) → TangentSpace I x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x, s x⟩ +-/ +#guard_msgs in +#check mfderiv I (I.prod 𝓘(𝕜, F)) (T% s) + +/-- +info: mfderiv% (T% s) : (x : B) → TangentSpace I x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x, s x⟩ +-/ +#guard_msgs in +#check mfderiv% (T% s) + +/-- info: mfderiv[u] (T% s) x₀ : TangentSpace I x₀ →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x₀, s x₀⟩ -/ +#guard_msgs in +#check mfderivWithin I (I.prod 𝓘(𝕜, F)) (T% s) u x₀ + +/-- info: mfderiv[u] (T% s) x₀ : TangentSpace I x₀ →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) ⟨x₀, s x₀⟩ -/ +#guard_msgs in +#check mfderiv[u] (T% s) x₀ + +/-- info: MDiff (T% s) : Prop -/ +#guard_msgs in +#check MDifferentiable I (I.prod 𝓘(𝕜, F)) (T% s) + +/-- info: MDiff (T% s) : Prop -/ +#guard_msgs in +#check MDiff (T% s) + +/-- info: MDiffAt (T% s) x₀ : Prop -/ +#guard_msgs in +#check MDifferentiableAt I (I.prod 𝓘(𝕜, F)) (T% s) x₀ + +/-- info: MDiffAt (T% s) x₀ : Prop -/ +#guard_msgs in +#check MDiffAt (T% s) x₀ + +/-- info: MDiff[u] (T% s) : Prop -/ +#guard_msgs in +#check MDifferentiableOn I (I.prod 𝓘(𝕜, F)) (T% s) u + +/-- info: MDiff[u] (T% s) : Prop -/ +#guard_msgs in +#check MDiff[u] (T% s) + +/-- info: MDiffAt[u] (T% s) x₀ : Prop -/ +#guard_msgs in +#check MDifferentiableWithinAt I (I.prod 𝓘(𝕜, F)) (T% s) u x₀ + +/-- info: MDiffAt[u] (T% s) x₀ : Prop -/ +#guard_msgs in +#check MDiffAt[u] (T% s) x₀ + +/-- info: HasMFDerivAt[u] (T% s) x₀ f' : Prop -/ +#guard_msgs in +#check HasMFDerivWithinAt I (I.prod 𝓘(𝕜, F)) (T% s) u x₀ f' + +/-- info: HasMFDerivAt[u] (T% s) x₀ f' : Prop -/ +#guard_msgs in +#check HasMFDerivAt[u] (T% s) x₀ f' + +/-- info: HasMFDerivAt% (T% s) x₀ f' : Prop -/ +#guard_msgs in +#check HasMFDerivAt I (I.prod 𝓘(𝕜, F)) (T% s) x₀ f' + +/-- info: HasMFDerivAt% (T% s) x₀ f' : Prop -/ +#guard_msgs in +#check HasMFDerivAt% (T% s) x₀ f' + +end TotalSpace + section ambiguity variable {g : E × E → M} in @@ -126,6 +255,7 @@ inst✝ : IsManifold I ∞ M f : M → M x : M s : Set M +f' : TangentSpace I x →L[ℝ] TangentSpace I (f x) v : (x : M) → TangentSpace I x g✝ g : E × E → E × E ⊢ MDiff id diff --git a/MathlibTest/DifferentialGeometry/Notation/PR40447.lean b/MathlibTest/DifferentialGeometry/Notation/PR40447.lean new file mode 100644 index 00000000000000..8b265aaa9becc2 --- /dev/null +++ b/MathlibTest/DifferentialGeometry/Notation/PR40447.lean @@ -0,0 +1,96 @@ +import Mathlib.Geometry.Manifold.Notation +import Mathlib.Geometry.Manifold.VectorBundle.Basic + +set_option pp.unicode.fun true + +open Bundle +open scoped Manifold + +-- Let `M` and `N` be smooth manifold. Suppose `V` is a vector bundle over `M` with model fiber `F`. +variable {𝕜 : Type*} [NontriviallyNormedField 𝕜] + {E : Type*} [NormedAddCommGroup E] [NormedSpace 𝕜 E] + {H : Type*} [TopologicalSpace H] (I : ModelWithCorners 𝕜 E H) + {M : Type*} [TopologicalSpace M] [ChartedSpace H M] + {E' : Type*} [NormedAddCommGroup E'] [NormedSpace 𝕜 E'] + {H' : Type*} [TopologicalSpace H'] (J : ModelWithCorners 𝕜 E' H') + {N : Type*} [TopologicalSpace N] [ChartedSpace H' N] + +variable (F : Type*) [NormedAddCommGroup F] [NormedSpace 𝕜 F] + (n : WithTop ℕ∞) + (V : M → Type*) [TopologicalSpace (TotalSpace F V)] + [∀ x, AddCommGroup (V x)] [∀ x, Module 𝕜 (V x)] + [∀ x : M, TopologicalSpace (V x)] [∀ x, IsTopologicalAddGroup (V x)] + [∀ x, ContinuousSMul 𝕜 (V x)] + [FiberBundle F V] [VectorBundle 𝕜 F V] + +-- Consider a function from `N` into the total space of `V`. +-- The correct model with corners to infer on the latter is `I.prod (𝓘(𝕜, F))` --- where +-- `I` is the model with corners on the base `M` of `V`. +/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) (f x) -/ +#guard_msgs in +variable {f : N → TotalSpace F V} {x : N} in +#check mfderiv J (I.prod 𝓘(𝕜, F)) f x + +-- The elaborators used to have a bug: they would always use the model on the domain `N` instead of +-- the model `I` on the bundle's base `M`. This works if `f` were a section of `V`, +-- but is incorrect in general! +/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (I.prod 𝓘(𝕜, F)) (f x) -/ +#guard_msgs in +variable {f : N → TotalSpace F V} {x : N} in #check mfderiv% f x + +-- Previously, projections like below were not supported: fixing the above bug properly also +-- addresses this. +/-- info: mfderiv% f x : TangentSpace (I.prod 𝓘(𝕜, F)) x →L[𝕜] TangentSpace J (f x) -/ +#guard_msgs in +variable {f : TotalSpace F V → N} {x : TotalSpace F V} in #check mfderiv% f x +/-- info: mfderiv% f x : TangentSpace (I.prod 𝓘(𝕜, F)) x →L[𝕜] TangentSpace J (f x) -/ +#guard_msgs in +variable {f : TotalSpace F V → N} {x : TotalSpace F V} in #check mfderiv (I.prod 𝓘(𝕜, F)) J f x + +-- Further tests for this feature. + +-- For a fiber bundle over a normed space, we still infer a model with corners. +/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F)) (f x) -/ +#guard_msgs in +variable {V' : E → Type*} [TopologicalSpace (TotalSpace F V')] [∀ x : E, TopologicalSpace (V' x)] + [FiberBundle F V'] {f : N → TotalSpace F V'} {x : N} in #check mfderiv% f x + +-- We don't do so for a fiber bundle over a product of normed spaces: there are several possible +-- choices for its base' model with corners. +/-- +error: Could not find a model with corners for `TotalSpace F V'`. + +Hint: failures to find a model with corners can be debugged with the command `set_option trace.Elab.DiffGeo.MDiff true`. +-/ +#guard_msgs in +variable {V' : (E × E')→ Type*} [TopologicalSpace (TotalSpace F V')] [∀ x, TopologicalSpace (V' x)] + [FiberBundle F V'] {f : N → TotalSpace F V'} {x : N} in +#check mfderiv% f x + + +-- Fiber bundles with more complicated fibers, e.g. products of normed spaces. + +/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F × F)) (f x) -/ +#guard_msgs in +variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F × F) V')] [∀ x : E, TopologicalSpace (V' x)] + [FiberBundle (F × F) V'] {f : N → TotalSpace (F × F) V'} {x : N} in #check mfderiv% f x + +/-- info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F →L[𝕜] F)) (f x) -/ +#guard_msgs in +variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F →L[𝕜] F) V')] [∀ x : E, TopologicalSpace (V' x)] + [FiberBundle (F →L[𝕜] F) V'] {f : N → TotalSpace (F →L[𝕜] F) V'} {x : N} in #check mfderiv% f x + +/-- +info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F × (F →L[𝕜] F))) (f x) +-/ +#guard_msgs in +variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F × (F →L[𝕜] F)) V')] [∀ x : E, TopologicalSpace (V' x)] + [FiberBundle (F × (F →L[𝕜] F)) V'] {f : N → TotalSpace (F × (F →L[𝕜] F)) V'} {x : N} in +#check mfderiv% f x + +/-- +info: mfderiv% f x : TangentSpace J x →L[𝕜] TangentSpace (𝓘(𝕜, E).prod 𝓘(𝕜, F × (F →L[𝕜] F →L[𝕜] F))) (f x) +-/ +#guard_msgs in +variable {V' : E → Type*} [TopologicalSpace (TotalSpace (F × (F →L[𝕜] F →L[𝕜] F)) V')] [∀ x : E, TopologicalSpace (V' x)] + [FiberBundle (F × (F →L[𝕜] F →L[𝕜] F)) V'] {f : N → TotalSpace (F × (F →L[𝕜] F →L[𝕜] F)) V'} {x : N} in #check mfderiv% f x diff --git a/MathlibTest/DifferentialGeometry/Notation/Sphere.lean b/MathlibTest/DifferentialGeometry/Notation/Sphere.lean index 8d22d6a5804056..854d1cd7dcfd97 100644 --- a/MathlibTest/DifferentialGeometry/Notation/Sphere.lean +++ b/MathlibTest/DifferentialGeometry/Notation/Sphere.lean @@ -230,10 +230,10 @@ trace: [Elab.DiffGeo.MDiff] Finding a model with corners for: `↑(Metric.sphere Found no fact `finrank ℝ E'' = n + 1` in the local context [Elab.DiffGeo.MDiff] 💥️ NormedField [Elab.DiffGeo.MDiff] Failed with error: - failed to synthesize instance of type class + failed to synthesize NontriviallyNormedField ↑(Metric.sphere 0 1) ⏎ - Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command. + Hint: Additional diagnostic information may be available using the `set_option diagnostics true` command. [Elab.DiffGeo.MDiff] 💥️ InnerProductSpace [Elab.DiffGeo.MDiff] Failed with error: Couldn't find an `InnerProductSpace` structure on `↑(Metric.sphere 0 1)` among local instances. diff --git a/MathlibTest/InstanceDiamonds.lean b/MathlibTest/InstanceDiamonds.lean index 291be1c646dcb5..17ead3a7c8b831 100644 --- a/MathlibTest/InstanceDiamonds.lean +++ b/MathlibTest/InstanceDiamonds.lean @@ -24,7 +24,7 @@ section SMul open scoped Polynomial -example : (SubNegMonoid.toZSMul : SMul ℤ ℂ) = (Complex.SMul.instSMulRealComplex : SMul ℤ ℂ) := by +example : (ZSMul.toSMul : SMul ℤ ℂ) = (Complex.SMul.instSMulRealComplex : SMul ℤ ℂ) := by with_reducible_and_instances rfl example : Module.restrictScalars ℝ ℂ ℂ = Complex.instModule := by @@ -35,19 +35,19 @@ example : Algebra.restrictScalars ℝ ℂ ℂ = Complex.instAlgebraOfReal := by rfl example (α β : Type _) [AddMonoid α] [AddMonoid β] : - (Prod.instSMul : SMul ℕ (α × β)) = AddMonoid.toNSMul := by + (Prod.instSMul : SMul ℕ (α × β)) = NSMul.toSMul := by with_reducible_and_instances rfl example (α β : Type _) [SubNegMonoid α] [SubNegMonoid β] : - (Prod.instSMul : SMul ℤ (α × β)) = SubNegMonoid.toZSMul := by + (Prod.instSMul : SMul ℤ (α × β)) = ZSMul.toSMul := by with_reducible_and_instances rfl example (α : Type _) (β : α → Type _) [∀ a, AddMonoid (β a)] : - (Pi.instSMul : SMul ℕ (∀ a, β a)) = AddMonoid.toNSMul := by + (Pi.instSMul : SMul ℕ (∀ a, β a)) = NSMul.toSMul := by with_reducible_and_instances rfl example (α : Type _) (β : α → Type _) [∀ a, SubNegMonoid (β a)] : - (Pi.instSMul : SMul ℤ (∀ a, β a)) = SubNegMonoid.toZSMul := by + (Pi.instSMul : SMul ℤ (∀ a, β a)) = ZSMul.toSMul := by with_reducible_and_instances rfl namespace TensorProduct diff --git a/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean b/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean index c38a1903bf7bc7..f6ea1a241f847d 100644 --- a/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean +++ b/MathlibTest/InstanceDiamonds/FieldTheory/IsAlgClosed/AlgebraicClosure.lean @@ -3,12 +3,10 @@ import Mathlib.FieldTheory.IsAlgClosed.AlgebraicClosure variable {k : Type*} [Field k] -set_option backward.isDefEq.respectTransparency false in example : (AddCommMonoid.toNatModule : Module ℕ (AlgebraicClosure k)) = @Algebra.toModule _ _ _ _ (AlgebraicClosure.instAlgebra k) := by with_reducible_and_instances rfl -set_option backward.isDefEq.respectTransparency false in example : (AddCommGroup.toIntModule _ : Module ℤ (AlgebraicClosure k)) = @Algebra.toModule _ _ _ _ (AlgebraicClosure.instAlgebra k) := by with_reducible_and_instances rfl diff --git a/MathlibTest/Lint.lean b/MathlibTest/Lint.lean deleted file mode 100644 index 391ebca8357334..00000000000000 --- a/MathlibTest/Lint.lean +++ /dev/null @@ -1,74 +0,0 @@ -import Batteries.Tactic.Alias -import Mathlib.Tactic.Linter.Lint -import Mathlib.Tactic.ToAdditive -/-- -warning: The namespace 'add' is duplicated in the declaration 'add.add' - -Note: This linter can be disabled with `set_option linter.dupNamespace false` --/ -#guard_msgs in -def add.add := True - -namespace Foo - -/-- -warning: The namespace 'Foo' is duplicated in the declaration 'Foo.Foo.foo' - -Note: This linter can be disabled with `set_option linter.dupNamespace false` --/ -#guard_msgs in -def Foo.foo := True - -set_option linter.translateRedundant false in -/-- -warning: The namespace 'add' is duplicated in the declaration 'Foo.add.add' - -Note: This linter can be disabled with `set_option linter.dupNamespace false` --/ -#guard_msgs in -set_option linter.dupNamespace true in -@[to_additive] theorem add.mul : True := .intro - --- However, the declaration `Foo.add.add` is present in the environment. -run_cmd Lean.Elab.Command.liftTermElabM do - let decl := (← Lean.getEnv).find? ``Foo.add.add - guard decl.isSome - -namespace Nat - -/-- -warning: The namespace 'Nat' is duplicated in the declaration 'Foo.Nat.Nat.Nats' - -Note: This linter can be disabled with `set_option linter.dupNamespace false` --/ -#guard_msgs in -alias Nat.Nats := Nat - -end Nat -end Foo - -namespace add - -/-- -warning: The namespace 'add' is duplicated in the declaration 'add.add' - -Note: This linter can be disabled with `set_option linter.dupNamespace false` ---- -warning: The namespace 'add' is duplicated in the declaration 'add.add' - -Note: This linter can be disabled with `set_option linter.dupNamespace false` --/ -#guard_msgs in -export Nat (add add_comm add) - -end add - -/-- -warning: The declaration 'double__underscore' contains '__', -which does not follow the mathlib naming conventions. Consider using single underscores instead. - -Note: This linter can be disabled with `set_option linter.style.nameCheck false` --/ -#guard_msgs in -set_option linter.style.nameCheck true in -theorem double__underscore : True := trivial diff --git a/MathlibTest/Linter/DocPrime.lean b/MathlibTest/Linter/DocPrime.lean index bc217c8686bc65..7f3139c7d0e153 100644 --- a/MathlibTest/Linter/DocPrime.lean +++ b/MathlibTest/Linter/DocPrime.lean @@ -88,6 +88,7 @@ Declarations whose name ends with a `'` are expected to contain an explanation f Note: This linter can be disabled with `set_option linter.docPrime false` -/ #guard_msgs in +set_option linter.defProp false in def def_no_doc' : True := .intro -- Anonymous declarations in a primed namespace should not get flagged by the linter. diff --git a/MathlibTest/Linter/DocString.lean b/MathlibTest/Linter/DocString.lean index 63dd55d1b24bd5..08269628e06d26 100644 --- a/MathlibTest/Linter/DocString.lean +++ b/MathlibTest/Linter/DocString.lean @@ -127,6 +127,23 @@ structure X where -/ y : Unit +-- Syntax quotation patterns involving `declModifiers` should not trigger a false +-- "empty docstring" warning. This is a regression test for a false positive where +-- `getDeclModifiers` would recurse into definition bodies and find `declModifiers` +-- nodes from syntax quotation patterns, treating their empty docstring slots as +-- actual empty docstrings. +section +open Lean Elab Command Parser Term in +#guard_msgs in +/-- A function that pattern-matches on `declModifiers`. -/ +def extractAttrs (stx : TSyntax ``Command.declModifiers) : + CommandElabM (Array <| TSyntax ``attrInstance) := do + match stx with + | `(declModifiers| $(_)? @[$[$atts],*] $(_)? $(_)? $(_)? $(_)?) => + return atts + | _ => return #[] +end + /-! # Tests for Verso-compatible docstrings -/ diff --git a/MathlibTest/DoubleUnderscore.lean b/MathlibTest/Linter/DoubleUnderscore.lean similarity index 64% rename from MathlibTest/DoubleUnderscore.lean rename to MathlibTest/Linter/DoubleUnderscore.lean index c345d518b3efc5..af1b7edaa42780 100644 --- a/MathlibTest/DoubleUnderscore.lean +++ b/MathlibTest/Linter/DoubleUnderscore.lean @@ -18,3 +18,12 @@ Note: This linter can be disabled with `set_option linter.style.nameCheck false` -/ #guard_msgs in def double__underscore : Unit := () + +/-- +warning: The declaration 'double__underscore'' contains '__', which does not follow the mathlib naming conventions. Consider using single underscores instead. + +Note: This linter can be disabled with `set_option linter.style.nameCheck false` +-/ +#guard_msgs in +set_option linter.style.nameCheck true in +theorem double__underscore' : True := trivial diff --git a/MathlibTest/Linter/DupNamespace.lean b/MathlibTest/Linter/DupNamespace.lean new file mode 100644 index 00000000000000..56e0180ba9b60d --- /dev/null +++ b/MathlibTest/Linter/DupNamespace.lean @@ -0,0 +1,172 @@ +import Batteries.Tactic.Alias +import Mathlib.Tactic.Linter.Lint +import Mathlib.Tactic.ToAdditive +/-- +warning: The namespace `add` is duplicated in the declaration `add.add`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +def add.add := True + +namespace Foo + +/-- +warning: The namespace `Foo` is duplicated in the declaration `Foo.Foo.foo`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +def Foo.foo := True + +set_option linter.translateRedundant false in +/-- +warning: The namespace `add` is duplicated in the declaration `Foo.add.add`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +set_option linter.dupNamespace true in +@[to_additive] theorem add.mul : True := .intro + +-- However, the declaration `Foo.add.add` is present in the environment. +run_cmd Lean.Elab.Command.liftTermElabM do + let decl := (← Lean.getEnv).find? ``Foo.add.add + guard decl.isSome + +namespace Nat + +/-- +warning: The namespace `Nat` is duplicated in the declaration `Foo.Nat.Nat.Nats`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +alias Nat.Nats := Nat + +end Nat +end Foo + +namespace add + +/-- +warning: The namespace `add` is duplicated in the declaration `add.add`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +--- +warning: The namespace `add` is duplicated in the declaration `add.add`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +export Nat (add add_comm add) + +end add + +/-- +warning: The namespace `Foo` is duplicated in the declaration `Foo.Foo`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Foo : True := trivial + +/-- +warning: The namespace `Foo` is duplicated in the declaration `Bar.Foo.Foo`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Bar.Foo.Foo : True := trivial + +/-- +warning: The namespace `Foo` is duplicated in the declaration `Foo.Foo.Bar`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Foo.Bar : True := trivial + +/-- +warning: The namespace `Foo` is duplicated in the declaration `Foo.Foo.Bar.Baz.hoge`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Foo.Bar.Baz.hoge : True := trivial + +#guard_msgs in +lemma Foo.Foos.Bar.Baz : True := trivial + +/-- +warning: The namespace `Foo` is duplicated in the declaration `Foo.Bar.Foo.baz`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Bar.Foo.baz : True := trivial + +/-- +warning: The namespaces `Foo` and `Bar` are duplicated in the declaration `Foo.Bar.Foo.Bar.baz`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Bar.Foo.Bar.baz : True := trivial + +/-- +warning: The namespaces `Foo` and `Baz` are duplicated in the declaration `Foo.Bar.Baz.Hoge.Foo.Baz.baz`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Bar.Baz.Hoge.Foo.Baz.baz : True := trivial + +/-- +warning: The namespaces `Foo` and `Bar` are duplicated in the declaration `Foo.Bar.Baz.Hoge.Foo.Bar.baz`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Bar.Baz.Hoge.Foo.Bar.baz : True := trivial + +/-- +warning: The namespaces `Foo`, `Bar`, and `Baz` are duplicated in the declaration `Foo.Bar.Baz.Hoge.Foo.Bar.Baz.az`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +lemma Foo.Bar.Baz.Hoge.Foo.Bar.Baz.az : True := trivial + +-- The linter detects the final name and not just what's written in the syntax. +namespace Foo.Bar +/-- +warning: The namespaces `Foo` and `Bar` are duplicated in the declaration `Foo.Bar.Foo.Bar.baz'`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +def Foo.Bar.baz' := 42 +end Foo.Bar + +-- We detect additional generated names. +/-- +warning: The namespace `AddSubgroup` is duplicated in the declaration `AddSubgroup.AddSubgroup.foo`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +@[to_additive AddSubgroup.AddSubgroup.foo] +def Subgroup.AddSubgroup.foo := 42 +-- (`AddSubgroup` is duplicated but only after translation) + +-- The linter works on deprecated decls: this is important +-- since people can forget to add a `_root_` when adding deprecations. +/-- +warning: The namespace `Foo` is duplicated in the declaration `Foo.Bar.Foo.baz'`. + +Note: This linter can be disabled with `set_option linter.dupNamespace false` +-/ +#guard_msgs in +@[deprecated "" (since := "")] +def Foo.Bar.Foo.baz' := 42 diff --git a/MathlibTest/Linter/Flexible/Basic.lean b/MathlibTest/Linter/Flexible/Basic.lean index a4bd116944c0ea..c4ea9105ae5abb 100644 --- a/MathlibTest/Linter/Flexible/Basic.lean +++ b/MathlibTest/Linter/Flexible/Basic.lean @@ -14,10 +14,12 @@ set_option linter.unusedVariables false This file contains basic tests for the flexible linter, which do not require any advanced imports. Anything which requires groups, rings or algebraic structures is considered advanced, and -tests for these can be found in `MathlibTest/ImportHeavyFlexibleLinter.lean` +tests for these can be found in `MathlibTest/Linter/Flexible/ImportHeavy.lean` +TODO: make output message appear only once for wildcard -/ + def n : Nat := 1 def m : Nat := 1 @@ -27,18 +29,33 @@ example : n = m := by simp [n] simp [m] +-- the given line number is correct /-- warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. Note: This linter can be disabled with `set_option linter.flexible false` --- -info: `exact h` uses `h`! +info: `exact h` +uses `h`, which was modified by the flexible tactic `simp` on line 43! -/ #guard_msgs in example (h : 0 + 0 = 0) : True := by simp at h try exact h +/-- +warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: `exact h` +uses `h`, which was modified by the flexible tactic `simp` on line +-/ +#guard_msgs (substring := true) in +example (h : 0 + 0 = 0) : True := by + simp at h + try exact h + /-- warning: `simp_all` is a flexible tactic modifying `⊢`. Try `simp_all?` and use the suggested `simp_all only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. @@ -47,13 +64,106 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp_all only [Nat.add_zero] --- -info: `exact Nat.le_succ_of_le h` uses `⊢`! +info: `exact Nat.le_succ_of_le h` +modifies the current goal, which was modified by the flexible tactic `simp_all` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example {a b : Nat} (h : a ≤ b) : a + 0 ≤ b + 1 := by simp_all exact Nat.le_succ_of_le h +/-- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: Try this: + [apply] simp only [Nat.add_zero] at * +--- +info: `exact Nat.le_succ_of_le h` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 98. +--- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: Try this: + [apply] simp only [Nat.add_zero] at * +--- +info: `exact Nat.le_succ_of_le h` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line +-/ +#guard_msgs (substring := true) in +example {a b : Nat} (h : a ≤ b) : a + 0 ≤ b + 1 := by + simp at * + exact Nat.le_succ_of_le h + +/-- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: `exact h2` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 118. +--- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: `exact h2` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line +-/ +#guard_msgs (substring := true) in +example {a b : Nat} (h1 : 0 + 0 = 0) (h2 : a ≤ b) : a ≤ b := by + simp at * + exact h2 + +/-- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: `exact h2` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 138. +--- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: `exact h2` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line +-/ +#guard_msgs (substring := true) in +example {a b : Nat} (h1 : 0 + 0 = 0) (h2 : a ≤ b) : a ≤ b := by + simp at * + exact h2 + +/-- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: Try this: + [apply] simp only [Nat.add_zero] at * +--- +info: `exact h` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line 164. +--- +warning: `simp at *` is a flexible tactic that potentially modifies all hypotheses and the current goal with a wildcard `*`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. + +Note: This linter can be disabled with `set_option linter.flexible false` +--- +info: Try this: + [apply] simp only [Nat.add_zero] at * +--- +info: `exact h` +uses a rigid tactic. Previously, the flexible tactic `simp`, which potentially modified all hypotheses and the goal with a wildcard `*`, was used on line +-/ +#guard_msgs (substring := true) in +example {a b : Nat} (h : a = b) : a + 0 = b := by + simp at * + exact h + -- `subst` does not use the goal #guard_msgs in example {a b : Nat} (h : a = b) : a + 0 = b := by @@ -78,7 +188,8 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.add_zero] --- -info: `assumption` uses `⊢`! +info: `assumption` +modifies the current goal, which was modified by the flexible tactic `simp` on line 206! --- warning: `simp` is a flexible tactic modifying `⊢`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. @@ -87,9 +198,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.add_zero] --- -info: `assumption` uses `⊢`! +info: `assumption` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example {a b : Nat} (h : a = b) : a + 0 = b := by simp induction a <;> assumption @@ -99,9 +211,10 @@ warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the Note: This linter can be disabled with `set_option linter.flexible false` --- -info: `exact h` uses `h`! +info: `exact h` +uses `h`, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example (h : 0 = 0 ∨ 0 = 0) : True := by cases h <;> rename_i h <;> @@ -117,7 +230,8 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.zero_ne_one, and_self] --- -info: `on_goal 2 => · contradiction` uses `⊢`! +info: `on_goal 2 => · contradiction` +modifies the current goal, which was modified by the flexible tactic `simp` on line 248! --- warning: `simp` is a flexible tactic modifying `⊢`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. @@ -126,9 +240,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.zero_ne_one, and_self] --- -info: `contradiction` uses `⊢`! +info: `contradiction` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example (h : 0 = 1 ∨ 0 = 1) : 0 = 1 ∧ 0 = 1 := by cases h <;> simp on_goal 2 => · contradiction @@ -146,7 +261,8 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.zero_ne_one, and_self] --- -info: `contradiction` uses `⊢`! +info: `contradiction` +modifies the current goal, which was modified by the flexible tactic `simp` on line 279! --- warning: `simp` is a flexible tactic modifying `⊢`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. @@ -155,9 +271,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.zero_ne_one, and_self] --- -info: `contradiction` uses `⊢`! +info: `contradiction` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example (h : 0 = 1 ∨ 0 = 1) : 0 = 1 ∧ 0 = 1 := by cases h <;> simp · contradiction @@ -168,15 +285,17 @@ warning: `simp at h k` is a flexible tactic modifying `k`. Try `simp?` and use t Note: This linter can be disabled with `set_option linter.flexible false` --- -info: `rw [← Classical.not_not (a := True)] at k` uses `k`! +info: `rw [← Classical.not_not (a := True)] at k` +uses `k`, which was modified by the flexible tactic `simp` on line 301! --- warning: `simp at h k` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. Note: This linter can be disabled with `set_option linter.flexible false` --- -info: `rw [← Classical.not_not (a := True)] at h` uses `h`! +info: `rw [← Classical.not_not (a := True)] at h` +uses `h`, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in -- `simp at h` stains `h` but not other locations example {h : 0 = 0} {k : 1 = 1} : True := by simp at h k; @@ -201,9 +320,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.add_zero] --- -info: `exact h.symm` uses `⊢`! +info: `exact h.symm` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in -- `congr` is allowed after `simp`, but "passes along the stain". example {a b : Nat} (h : a = b) : a + b + 0 = b + a := by simp @@ -248,9 +368,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.zero_ne_one, and_self] --- -info: `contradiction` uses `⊢`! +info: `contradiction` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example (h : 0 = 1 ∨ 0 = 1) : 0 = 1 ∧ 0 = 1 := by cases h <;> simp · simp_all @@ -277,9 +398,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [not_true_eq_false, not_false_eq_true] at h --- -info: `rw [← Classical.not_not (a := True)] at h` uses `h`! +info: `rw [← Classical.not_not (a := True)] at h` +uses `h`, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in -- `simp at h` stains `h` but not other locations example {h : 0 = 0} {k : 1 = 1} : ¬ ¬ True := by simp at h @@ -294,15 +416,17 @@ warning: `simp at h k` is a flexible tactic modifying `k`. Try `simp?` and use t Note: This linter can be disabled with `set_option linter.flexible false` --- -info: `rw [← Classical.not_not (a := True)] at k` uses `k`! +info: `rw [← Classical.not_not (a := True)] at k` +uses `k`, which was modified by the flexible tactic `simp` on line 432! --- warning: `simp at h k` is a flexible tactic modifying `h`. Try `simp?` and use the suggested `simp only [...]`. Alternatively, use `suffices` to explicitly state the simplified form. Note: This linter can be disabled with `set_option linter.flexible false` --- -info: `rw [← Classical.not_not (a := True)] at h` uses `h`! +info: `rw [← Classical.not_not (a := True)] at h` +uses `h`, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in -- `simp at h` stains `h` but not other locations example {h : 0 = 0} {k : 1 = 1} : True := by simp at h k @@ -317,9 +441,10 @@ warning: `simp at h` is a flexible tactic modifying `h`. Try `simp?` and use the Note: This linter can be disabled with `set_option linter.flexible false` --- -info: `rw [← Classical.not_not (a := True)] at h` uses `h`! +info: `rw [← Classical.not_not (a := True)] at h` +uses `h`, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in -- `simp at h` stains `h` but not other locations example {h : 0 = 0} : True := by simp at h @@ -336,9 +461,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.zero_ne_one] --- -info: `rwa [← Classical.not_not (a := False)]` uses `⊢`! +info: `rwa [← Classical.not_not (a := False)]` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example {h : False} : 0 = 1 := by simp rw [← Classical.not_not (a := False)] at h @@ -353,9 +479,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Nat.zero_ne_one] --- -info: `rwa [← Classical.not_not (a := False)]` uses `⊢`! +info: `rwa [← Classical.not_not (a := False)]` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example {h : False} : 0 = 1 ∧ 0 = 1 := by constructor · simpa diff --git a/MathlibTest/Linter/Flexible/ImportHeavy.lean b/MathlibTest/Linter/Flexible/ImportHeavy.lean index 59b961058a98f6..b9d0f275ff350a 100644 --- a/MathlibTest/Linter/Flexible/ImportHeavy.lean +++ b/MathlibTest/Linter/Flexible/ImportHeavy.lean @@ -46,9 +46,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [zero_add] --- -info: `rw [add_comm]` uses `⊢`! +info: `rw [add_comm]` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in -- `norm_num` is allowed after `simp`, but "passes along the stain". example {a : Rat} : a + (0 + 2 + 1 : Rat) = 3 + a := by simp @@ -81,9 +82,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [mul_zero, add_zero] --- -info: `positivity` uses `⊢`! +info: `positivity` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example {k l : ℤ} : 0 ≤ k ^ 2 + 4 * l * 0 := by simp positivity @@ -124,9 +126,10 @@ Note: This linter can be disabled with `set_option linter.flexible false` info: Try this: [apply] simp only [Function.comp_apply, add_zero] --- -info: `fun_prop` uses `⊢`! +info: `fun_prop` +modifies the current goal, which was modified by the flexible tactic `simp` on line -/ -#guard_msgs in +#guard_msgs (substring := true) in example {X : Type*} [TopologicalSpace X] {f : X → ℕ} {g : ℕ → X} (hf : Continuous f) (hg : Continuous g) : Continuous (fun x ↦ (f ∘ g) x + 0) := by diff --git a/MathlibTest/globalAttributeIn.lean b/MathlibTest/Linter/GlobalAttributeIn.lean similarity index 98% rename from MathlibTest/globalAttributeIn.lean rename to MathlibTest/Linter/GlobalAttributeIn.lean index 1c124f2b4d4ed0..ce50c8fece0e58 100644 --- a/MathlibTest/globalAttributeIn.lean +++ b/MathlibTest/Linter/GlobalAttributeIn.lean @@ -13,6 +13,7 @@ import Mathlib.Tactic.Linter.GlobalAttributeIn class Dummy where field : True +set_option linter.defProp false in @[reducible] def dummyInst : Dummy := ⟨True.intro⟩ /-- diff --git a/MathlibTest/HashCommandLinter.lean b/MathlibTest/Linter/HashCommand.lean similarity index 100% rename from MathlibTest/HashCommandLinter.lean rename to MathlibTest/Linter/HashCommand.lean diff --git a/MathlibTest/HaveLetLinter.lean b/MathlibTest/Linter/HaveLet.lean similarity index 100% rename from MathlibTest/HaveLetLinter.lean rename to MathlibTest/Linter/HaveLet.lean diff --git a/MathlibTest/Header.lean b/MathlibTest/Linter/Header/Basic.lean similarity index 100% rename from MathlibTest/Header.lean rename to MathlibTest/Linter/Header/Basic.lean diff --git a/MathlibTest/HeaderFail.lean b/MathlibTest/Linter/Header/Fail.lean similarity index 100% rename from MathlibTest/HeaderFail.lean rename to MathlibTest/Linter/Header/Fail.lean diff --git a/MathlibTest/VersoHeader.lean b/MathlibTest/Linter/Header/Verso.lean similarity index 100% rename from MathlibTest/VersoHeader.lean rename to MathlibTest/Linter/Header/Verso.lean diff --git a/MathlibTest/LongFile.lean b/MathlibTest/Linter/LongFile.lean similarity index 100% rename from MathlibTest/LongFile.lean rename to MathlibTest/Linter/LongFile.lean diff --git a/MathlibTest/Multigoal.lean b/MathlibTest/Linter/Multigoal.lean similarity index 100% rename from MathlibTest/Multigoal.lean rename to MathlibTest/Linter/Multigoal.lean diff --git a/MathlibTest/UnusedTactic.lean b/MathlibTest/Linter/UnusedTactic.lean similarity index 97% rename from MathlibTest/UnusedTactic.lean rename to MathlibTest/Linter/UnusedTactic.lean index 812ffe92e2c33b..2991ffbb29eaca 100644 --- a/MathlibTest/UnusedTactic.lean +++ b/MathlibTest/Linter/UnusedTactic.lean @@ -20,6 +20,7 @@ example : 1 = 1 := by change 1 = 1 rfl +set_option linter.defProp false in def why2 : True → True := (by refine ·) example : True := by diff --git a/MathlibTest/WhitespaceLinter.lean b/MathlibTest/Linter/Whitespace.lean similarity index 100% rename from MathlibTest/WhitespaceLinter.lean rename to MathlibTest/Linter/Whitespace.lean diff --git a/MathlibTest/MinImports.lean b/MathlibTest/MinImports.lean index e9b070f1862af7..5d1ce54b16f228 100644 --- a/MathlibTest/MinImports.lean +++ b/MathlibTest/MinImports.lean @@ -86,6 +86,7 @@ lemma hi (n : ℕ) : n = n := by extract_goal; rfl section Variables +set_option linter.defProp false in /-- info: public import Mathlib.Data.Nat.Notation -/ #guard_msgs in #min_imports in @@ -98,6 +99,7 @@ variable {K : Type*} [Field K] namespace Namespace +set_option linter.defProp false in -- The dependency on `Semiring` is only found in the `variable` declaration. -- We find it by looking up the declaration by name and checking the term, -- which used to get confused if running in a namespace. diff --git a/MathlibTest/MkIffOfInductive.lean b/MathlibTest/MkIffOfInductive.lean index d0a602924fa8de..cd958cdc29656d 100644 --- a/MathlibTest/MkIffOfInductive.lean +++ b/MathlibTest/MkIffOfInductive.lean @@ -79,4 +79,4 @@ inductive ReflTransGen {α : Type _} (r : α → α → Prop) (a : α) : α → example {α : Type} (r : α → α → Prop) (a c : α) : ReflTransGen r a c ↔ c = a ∨ ∃ b : α, ReflTransGen r a b ∧ r b c := - reflTransGen_iff r a c + reflTransGen_iff r a c diff --git a/MathlibTest/Simps.lean b/MathlibTest/Simps.lean index b315b0cb6598ad..e37261b0427cc4 100644 --- a/MathlibTest/Simps.lean +++ b/MathlibTest/Simps.lean @@ -977,6 +977,7 @@ instance has_PropClass (n : ℕ) : PropClass n := ⟨trivial⟩ structure NeedsPropClass (n : ℕ) [PropClass n] where (t : True) +set_option linter.defProp false in @[simps] def test_PropClass : NeedsPropClass 1 := { t := trivial } diff --git a/MathlibTest/Subsingleton.lean b/MathlibTest/Subsingleton.lean index f139fe25b410b0..eca165c5f4f5ac 100644 --- a/MathlibTest/Subsingleton.lean +++ b/MathlibTest/Subsingleton.lean @@ -141,6 +141,7 @@ example {α : Type} [BEq α] (f : ∀ {β : Type} [BEq β], Subsingleton β) (x The same, but now there's a universe level metavariable. -/ set_option warn.classDefReducibility false in +set_option linter.defProp false in def fdef : ∀ {β : Type _} [BEq β], Subsingleton β := test_sorry example {α : Type} [BEq α] (x y : α) : x = y := by diff --git a/MathlibTest/Tactic/Convert/Basic.lean b/MathlibTest/Tactic/Convert/Basic.lean index 6c41f450e271e8..a6ccf70ca7e7a3 100644 --- a/MathlibTest/Tactic/Convert/Basic.lean +++ b/MathlibTest/Tactic/Convert/Basic.lean @@ -2,6 +2,7 @@ module import Mathlib.Tactic.Convert import Mathlib.Algebra.Group.Basic import Mathlib.Data.Set.Image +import Mathlib.Algebra.Notation.Pi.Defs private axiom test_sorry : ∀ {α}, α set_option autoImplicit true @@ -153,4 +154,10 @@ example (P : ℕ → Prop) {a b : ℕ} (hab : b = a) (h : P a) : P (semireducibl example (P : ℕ → Prop) {a b : ℕ} (hab : b = a) (h : P (semireducibleId a)) : P b := by convert! h +-- Test that overapplications are supported +example (f g h k : Nat → Nat) (H : (f + g) 1 = 0) : (h + k) 1 = 0 := by + convert H + guard_target =ₛ h = f + all_goals exact test_sorry + end Tests diff --git a/MathlibTest/Tactic/GRewrite.lean b/MathlibTest/Tactic/GRewrite.lean index 634586f7fad85e..62a69dee5f6a3f 100644 --- a/MathlibTest/Tactic/GRewrite.lean +++ b/MathlibTest/Tactic/GRewrite.lean @@ -48,8 +48,8 @@ example (h₁ : c ≤ b) (h₂ : a + 5 < c + 6) : a + 5 < b + 6 := by 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₁ + guard_hyp h₁ :ₛ a + e < d + e + exact le_of_lt h₁ example (f g : α → α) (h : ∀ x : α, f x ≤ g x) (h₂ : g a + g b ≤ 5) : f a + f b ≤ 5 := by grw [h] @@ -84,15 +84,9 @@ example (h₁ : a ≤ b) : a * c ≤ b * c := by 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] - fail_if_success guard_target =ₛ 2 * y < z - guard_target = 2 * y < z + guard_target = 2 * y ≤ z exact test_sorry end inequalities @@ -126,11 +120,11 @@ a b : ℕ h : a < b f : ℕ → ℕ hf : ∀ (i : ℕ), 0 ≤ f i -⊢ ∑ i ∈ {a | a ≤ b}.toFinset, f i ≤ ∑ i ∈ {x | x ≤ b}.toFinset, f i +⊢ ∑ i ∈ {a | a < b}.toFinset, f i ≤ ∑ i ∈ {x | x < b}.toFinset, f i -/ #guard_msgs in example {a b : Nat} (h : a < b) (f : Nat → Nat) (hf : ∀ i, 0 ≤ f i) : - ∑ j ∈ ({z | z ≤ a} : Set Nat), f j ≤ ∑ i ∈ ({x | x ≤ b} : Set Nat), f i := by + ∑ j ∈ ({z | z ≤ a} : Set Nat), f j ≤ ∑ i ∈ ({x | x < b} : Set Nat), f i := by grewrite [h] trace_state rfl @@ -142,7 +136,7 @@ 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₃ + exact le_of_lt h₃ example {x y z : ℚ} (f g : ℚ → ℚ) (h : ∀ t, f t = g t) : 2 * f x * f y * f x ≤ z := by grw [h] @@ -201,7 +195,7 @@ example {a b : ℤ} (h1 : a ≡ 3 [ZMOD 5]) (h2 : b ≡ a ^ 2 + 1 [ZMOD 5]) : 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_hyp h1 : a < 3 * y guard_target = 2 * y ≤ b exact test_sorry @@ -344,7 +338,7 @@ example : ∃ n, n < 2 := by refine ⟨?_, ?_⟩ on_goal 2 => grw [← one_lt_two] exact 0 - refine zero_lt_one + refine zero_le_one section zmod @@ -454,3 +448,55 @@ example {a b c d e f g h i j k : Rat} : a * b * c * d * e * f * g * h * i * j * exact test_sorry end cache + +section strict + +variable {α : Type u} [LinearOrder α] {a b c d : α} + +example (h₁ : a < b) (h₂ : b ≤ c) : a < c := by + grw [h₁, h₂] + +example (h₁ : a < b) (h₂ : b ≤ c) : a < c := by + grw [← h₂, ← h₁] + +example (h₁ : a ≤ b) (h₂ : b < c) : a < c := by + grw [h₁, h₂] + +example (h₁ : a ≤ b) (h₂ : b < c) : a < c := by + grw [← h₂, ← h₁] + +example (h₁ : a < b) (h₂ : b ≤ c) : a < c := by + by_contra!; grw [h₁, h₂] at this; contrapose! this; rfl + +example (h₁ : a < b) (h₂ : b ≤ c) : a < c := by + by_contra!; grw [← h₂, ← h₁] at this; contrapose! this; rfl + +example (h₁ : a ≤ b) (h₂ : b < c) : a < c := by + by_contra!; grw [h₁, h₂] at this; contrapose! this; rfl + +example (h₁ : a ≤ b) (h₂ : b < c) : a < c := by + by_contra!; grw [← h₂, ← h₁] at this; contrapose! this; rfl + +-- Strict inequalities can also be used as non-strict ones: +example (h₁ : a < b) (h₂ : b < c) : a ≤ c := by + grw [h₁, h₂] + +variable [CommRing α] [IsStrictOrderedRing α] in +example (h : a < b) (_ : 0 ≤ a) : 1 + 2 * a ^ 2 < 9 := by + grw [h] + guard_target = 1 + 2 * b ^ 2 ≤ 9 + exact test_sorry + +example (h₁ : a < b) (h₂ : c < d) : Set.Icc b c ⊆ Set.Ioo a d := by + grw [h₁, h₂] + +example (h₁ : a < b) (h₂ : c < d) : Set.Icc b c ⊆ Set.Ioo a d := by + grw [h₂, h₁] + +example (h₁ : a < b) : Set.Iic a ⊆ Set.Iio b := by + grw [h₁] + +example (h₁ : a < b) : Set.Ici b ⊆ Set.Ioi a := by + grw [h₁] + +end strict diff --git a/MathlibTest/Tactic/NormNum/Basic.lean b/MathlibTest/Tactic/NormNum/Basic.lean index 91681f9558d95f..f46bd5b3073eb4 100644 --- a/MathlibTest/Tactic/NormNum/Basic.lean +++ b/MathlibTest/Tactic/NormNum/Basic.lean @@ -710,7 +710,7 @@ example : - ((94 * 89) + (79 - (23 - (((- 1 / 55) + 95) * (28 - (54 / - - - 22)) example : (- 23 + 61) = (38 : α) := by norm_num1 example : - (93 / 69) = (-31/23 : α) := by norm_num1 example : (- - ((68 / (39 + (((45 * - (59 - (37 + 35))) / (53 - 75)) - - - (100 + - (50 / (- 30 - 59)))))) - (69 - (23 * 30))) / (57 + 17)) = (137496481/16368578 : α) := by + - (100 + - (50 / (- 30 - 59)))))) - (69 - (23 * 30))) / (57 + 17)) = (137496481/16368578 : α) := by norm_num1 example : (- 19 * - - (75 * - - 41)) = (-58425 : α) := by norm_num1 example : ((3 / ((- 28 * 45) * (19 + ((- (- 88 - (- (- 1 + 90) + 8)) + 87) * 48)))) + 1) = diff --git a/MathlibTest/Tactic/Polynomial.lean b/MathlibTest/Tactic/Polynomial.lean new file mode 100644 index 00000000000000..72444858f74bb8 --- /dev/null +++ b/MathlibTest/Tactic/Polynomial.lean @@ -0,0 +1,82 @@ +module +import Mathlib.Tactic.Polynomial.Basic +import Mathlib.RingTheory.MvPolynomial + + +/-! # The `polynomial` tactic -/ + +axiom sorryPolynomialTest {P : Prop} : P + +section poly +open Polynomial + +example (a : ℚ) : (X + C a)^2 = X^2 + C (2*a) * X + C (a^2) := by + polynomial + +example (a : ℚ) : (X + C a)^2 = X^2 + (2*a) • X + C (a^2) := by + polynomial + +example (a : ℚ) : (2*X + C a)^2 = 4 * monomial 2 1 + monomial 1 (4*a) + monomial 0 (a^2) := by + polynomial + +example (a : ℚ) : (X - C a)*(X + C a) = X^2 - C (a^2) := by + polynomial + +example (a : ℚ) : (C a * X + C 4)^2 = 0 := by + polynomial_nf + guard_target = C 16 + C (a * 8) * X + C (a ^ 2) * X ^ 2 = 0 + apply sorryPolynomialTest + +example (a b c : ℚ) : (X + C a)^2 = X^2 + C c * X + C b := by + polynomial_nf + guard_target = C (a ^ 2) + C (a * 2) * X + X ^ 2 = C b + C c * X + X ^ 2 + apply sorryPolynomialTest + +example (a : ℚ) (n : ℕ) : (X^n + C a)^2 = 0 := by + polynomial_nf + guard_target = C (a ^ 2) + C (a * 2) * X ^ n + X ^ (n * 2) = 0 + apply sorryPolynomialTest + +variable {R A : Type*} [CommRing R] [CommRing A] [Algebra R A] {r₁ : R} {a₁ : A} in +example : Polynomial.map (algebraMap R A) (C r₁ * X) = C a₁ * X := by + polynomial_nf + guard_target = C ((algebraMap R A) r₁) * X = C a₁ * X + apply sorryPolynomialTest + +example {P : ℚ[X] → Prop} (h : P (X ^ 2 + X + C 4⁻¹)) : P ((X + C 2⁻¹) ^ 2) := by + polynomial_nf at h ⊢ + exact h + +end poly + +section mvpoly +open MvPolynomial + +example (a : ℚ) : (X 0 + C a)^2 = X 0^2 + C (2*a) * X 0 + C (a^2) := by + polynomial + +example (a : ℚ) : (X 0 + C a)^2 = X 0^2 + (2*a) • X 0 + C (a^2) := by + polynomial + +example (a : ℚ) : (X 0 - C a)*(X 0 + C a) = (X 0)^2 - C (a^2) := by + polynomial + +example (a : ℚ) : (X 0 - X 1 * C a)*(X 0 + X 1 * C a) = (X 0)^2 - (X 1) ^ 2 * C (a^2) := by + polynomial + +example (a : ℚ) : ((X 0 + C a)^2).eval (fun _ ↦ -a) = 0 := by + polynomial_nf + guard_target = (eval fun i => -a) (C (a ^ 2) + C (a * 2) * X 0 + X 0 ^ 2) = 0 + apply sorryPolynomialTest + +example (a b c : ℤ) : (X 0 * C a + X 1 * X 37 * C (b*(c-1)))^2 * (X 0 - 1) = 0 := by + polynomial_nf + guard_target = C (a * b * 2 - a * b * c * 2) * (X 0 * X 1 * X 37) + + C (b ^ 2 - b ^ 2 * c * 2 + b ^ 2 * c ^ 2) * (X 0 * X 1 ^ 2 * X 37 ^ 2) + + C (-a ^ 2) * X 0 ^ 2 + + C (-(a * b * 2) + a * b * c * 2) * (X 0 ^ 2 * X 1 * X 37) + + C (a ^ 2) * X 0 ^ 3 + + C (-b ^ 2 + (b ^ 2 * c * 2 - b ^ 2 * c ^ 2)) * (X 1 ^ 2 * X 37 ^ 2) = 0 + apply sorryPolynomialTest + +end mvpoly diff --git a/MathlibTest/Tactic/Push/Basic.lean b/MathlibTest/Tactic/Push/Basic.lean index b1827f78c30fdd..ffcb6348d50aa1 100644 --- a/MathlibTest/Tactic/Push/Basic.lean +++ b/MathlibTest/Tactic/Push/Basic.lean @@ -1,4 +1,5 @@ module + import Mathlib.Tactic.Push import Mathlib.Data.Nat.Cast.Basic import Mathlib.Data.Set.Basic @@ -23,6 +24,10 @@ variable {p q r : Prop} #guard_msgs in #push _ ∧ _ => (p ∨ True) ∧ (q ∨ r) +/-- info: (∃ x x_1, x ≠ x_1) ∨ True -/ +#guard_msgs in +#push ∃ _, _ => ∃ a : Nat, ∃ b, a ≠ b ∨ True + example {r : ℕ → Prop} : ∀ n : ℕ, p ∨ r n ∧ q ∧ n = 1 := by push ∀ n, _ guard_target =ₛ p ∨ (∀ n, r n) ∧ q ∧ ∀ n : ℕ, n = 1 diff --git a/MathlibTest/Tactic/Says/Basic.lean b/MathlibTest/Tactic/Says/Basic.lean index e21eac40686df1..1ab8f8f01ae9b7 100644 --- a/MathlibTest/Tactic/Says/Basic.lean +++ b/MathlibTest/Tactic/Says/Basic.lean @@ -97,8 +97,10 @@ example : True := by -- Check that verification works even with multi-line suggestions, as produced by aesop def P : Prop := True def Q : Prop := True +set_option linter.defProp false in @[simp] def very_long_lemma_name_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa : Q → P := fun _ => trivial +set_option linter.defProp false in @[simp] def very_long_lemma_name_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb : Q := trivial /-- diff --git a/MathlibTest/ValidatePRTitle.lean b/MathlibTest/ValidatePRTitle.lean index aacda293a7e90d..d7b20b55b20290 100644 --- a/MathlibTest/ValidatePRTitle.lean +++ b/MathlibTest/ValidatePRTitle.lean @@ -114,16 +114,35 @@ section subject #guard_msgs in #check_title "feat: bad title." -/-- info: Message: 'error: the PR subject should be lowercased' -/ +/-- info: Message: 'error: the PR subject `My Bad Title` should be lowercased' -/ #guard_msgs in #check_title "feat: My Bad Title" -- Starting with an acronym is fine, however. #guard_msgs in #check_title "feat: RPC acronyms are fine" +#guard_msgs in #check_title "feat: `ℕ` is countable" +-- We also allow a `s`, `'s` or `'ed` suffix, as manual heuristic. +#guard_msgs in #check_title "feat: RPCs are yellow" +#guard_msgs in #check_title "chore: LLMs require adjusting our policies" +#guard_msgs in #check_title "chore: PR'ed lemmas" +#guard_msgs in #check_title "feat(CI): PR's to be deleted" +#guard_msgs in #check_title "chore: FCP'ed decisions should be listed separately" +-- We only remove at most one suffix. +/-- info: Message: 'error: the PR subject `PR'ed's lemmas` should be lowercased' -/ +#guard_msgs in #check_title "chore: PR'ed's lemmas" +/-- info: Message: 'error: the PR subject `PRs's something` should be lowercased' -/ +#guard_msgs in #check_title "chore: PRs's something" +-- This is not quite an abbreviation (and a grammar error). +/-- +info: Message: 'error: the PR subject `FCPed decisions should be listed separately` should be lowercased' +-/ +#guard_msgs in #check_title "chore: FCPed decisions should be listed separately" -- This PR title is arguably not very bad (Lindelöf is a proper name), -- a better fix is to start with a verb (which you should do anyway.) -/-- info: Message: 'error: the PR subject should be lowercased' -/ +/-- +info: Message: 'error: the PR subject `Lindelöf spaces something something` should be lowercased' +-/ #guard_msgs in #check_title "feat: Lindelöf spaces something something" @@ -186,3 +205,23 @@ info: Message: 'error: the PR title contains multiple consecutive spaces; please -/ #guard_msgs in #check_title "feat(Mathlib/Algebra.lean): title." + +#guard_msgs in +#check_title "feat(ModularForm): E2 is bounded at ImInfty" + +#guard_msgs in +#check_title "feat(ModuleForm): 2E is bounded at ImInfty" + +#guard_msgs in +#check_title "feat(ModuleForm): 2e is less than 6" + +/-- info: Message: 'error: the PR subject `W3c` should be lowercased' -/ +#guard_msgs in +#check_title "feat(ModuleForm): W3c" + +#guard_msgs in +#check_title "feat(ModuleForm): W3C" + +/-- info: Message: 'error: the PR subject `A new lemma` should be lowercased' -/ +#guard_msgs in +#check_title "feat(ModuleForm): A new lemma" diff --git a/MathlibTest/matrix.lean b/MathlibTest/matrix.lean index 88ffc5b1fc731e..d97533ec18b060 100644 --- a/MathlibTest/matrix.lean +++ b/MathlibTest/matrix.lean @@ -4,7 +4,10 @@ https://github.com/leanprover-community/mathlib/blob/4f4a1c875d0baa92ab5d92f3fb1 -/ import Mathlib.GroupTheory.Perm.Fin import Mathlib.LinearAlgebra.Matrix.Determinant.Basic +import Mathlib.LinearAlgebra.Matrix.Determinant.Bird import Mathlib.LinearAlgebra.Matrix.Notation +import Mathlib.RingTheory.Polynomial.Basic +import Mathlib.Tactic.Determinant.Bird import Qq open Qq @@ -186,4 +189,65 @@ example (ι : Type*) [Inhabited ι] : Matrix.replicateCol ι (fun (_ : Fin 3) => simp_all rfl +section BirdDet + +open BirdDet + +variable + {R : Type*} + [CommRing R] + +example : birdDet 0 #[] = (1 : ℤ) := by + eval_det + +example : birdDet 1 #[-1] = -1 := by + eval_det + +example : birdDet 2 #[1, 2, 3, 4] = -2 := by + eval_det + +example : birdDet 2 (let A := #[1, 2, 3, 4]; A) = -2 := by + eval_det + +example (a b c d : R) : + birdDet 2 #[a, b, c, d] = a * d - b * c := by + eval_det + ring + +example (a b c d : R) : + birdDet 2 #[a, b, c, d] = a * d - b * c := by + simp only [norm_det] + ring + +example : birdDet 2 #[1, 2, 2, 4] + birdDet 2 #[2, 3, 4, 5] = -2 := by + simp only [norm_det] + norm_num + +example : birdDet 2 #[birdDet 2 #[2, 3, 4, 5], 2, 2, 4] = -12 := by + simp only [norm_det] + +example : + birdDet 8 + #[ 2, 0, -1, 0, 0, 0, 0, 0, + 0, 2, 0, -1, 0, 0, 0, 0, + -1, 0, 2, -1, 0, 0, 0, 0, + 0, -1, -1, 2, -1, 0, 0, 0, + 0, 0, 0, -1, 2, -1, 0, 0, + 0, 0, 0, 0, -1, 2, -1, 0, + 0, 0, 0, 0, 0, -1, 2, -1, + 0, 0, 0, 0, 0, 0, -1, 2] = 1 := by + simp only [norm_det] + +open MvPolynomial in +lemma test_case_11 : + birdDet (R := MvPolynomial (Fin 3) R) + 3 + #[1 , X 0, (X 0) ^ 2, + 1 , X 1, (X 1) ^ 2, + 1 , X 2, (X 2) ^ 2] = (X 0 - X 1) * (X 1 - X 2) * (X 2 - X 0) := by + simp only [norm_det] + ring + +end BirdDet + end Matrix diff --git a/MathlibTest/symm.lean b/MathlibTest/symm.lean index 4ae3ef96355bc3..6e660b12b59b5b 100644 --- a/MathlibTest/symm.lean +++ b/MathlibTest/symm.lean @@ -3,6 +3,7 @@ import Mathlib.Logic.Equiv.Basic set_option autoImplicit true -- testing that the attribute is recognized +set_option linter.defProp false in @[symm] def eq_symm {α : Type} (a b : α) : a = b → b = a := Eq.symm example (a b : Nat) : a = b → b = a := by intros; symm; assumption @@ -11,6 +12,7 @@ example (a b : Nat) : a = b → True → b = a := by intro h _; symm at h; assum def sameParity : Nat → Nat → Prop | n, m => n % 2 = m % 2 +set_option linter.defProp false in @[symm] def sameParity_symm (n m : Nat) : sameParity n m → sameParity m n := Eq.symm example (a b : Nat) : sameParity a b → sameParity b a := by intros; symm; assumption diff --git a/bors.toml b/bors.toml index 0dd32adf0ec53e..acdb67c060ed84 100644 --- a/bors.toml +++ b/bors.toml @@ -1,4 +1,4 @@ -status = ["ci (staging) / Build", "ci (staging) / Lint style", "ci (staging) / Post-Build Step", "ci (staging) / Post-CI job"] +status = ["ci (staging) / Build", "ci (staging) / Test and lint", "ci (staging) / Lint style", "ci (staging) / Post-Build Step", "ci (staging) / Post-CI job"] use_squash_merge = true timeout_sec = 7200 block_labels = ["WIP", "blocked-by-other-PR", "merge-conflict", "awaiting-CI"] @@ -6,6 +6,13 @@ delete_merged_branches = true update_base_for_deletes = true cut_body_after = "\n---" max_batch_size = 16 + +[labels] +on_queue = "ready-to-merge" # PR is on the merge queue (a batch waiting or running) +building = "bors-staging" # PR is currently building on staging (such PRs will also have the on_queue label) +failed = "awaiting-requeue" # PR's merge build failed terminally and was dropped; needs a re-queue +delegated = "delegated" # PR has at least one active (non-expired) delegation + [delegation] default_expiry_sec = 1209600 # 2 weeks # Entries below are Erlang `:glob` patterns (NOT gitignore): `*` matches across `/`, diff --git a/docs/100.yaml b/docs/100.yaml index 84f6227e311369..17a09b6ceb64c1 100644 --- a/docs/100.yaml +++ b/docs/100.yaml @@ -146,6 +146,7 @@ decls : - taylor_mean_remainder_lagrange - taylor_mean_remainder_cauchy + - map_add_eq_sum_add_integral_iteratedFDeriv authors: Moritz Doll 36: title : Brouwer Fixed Point Theorem diff --git a/docs/1000.yaml b/docs/1000.yaml index 55f54757b9fe15..3b3cef90ecd4b2 100644 --- a/docs/1000.yaml +++ b/docs/1000.yaml @@ -241,6 +241,7 @@ Q225973: Q226014: title: Poincaré recurrence theorem + decl: MeasureTheory.Conservative.ae_mem_imp_frequently_image_mem Q230848: title: Lamé’s theorem @@ -365,6 +366,7 @@ Q332465: Q338886: title: Divergence theorem + decl: MeasureTheory.integral_divergence_of_hasFDerivAt_off_countable Q339495: title: Symphonic theorem @@ -681,7 +683,7 @@ Q656176: Q656198: title: Maschke's theorem - # `RepresentationTheory/Maschke` comes very close, but doesn't prove the standard form yet... + decl: MonoidAlgebra.Submodule.exists_isCompl Q656645: title: Hilbert's basis theorem @@ -909,9 +911,8 @@ Q830513: Q834025: title: Cauchy integral theorem - decl: Complex.circleIntegral_div_sub_of_differentiable_on_off_countable - authors: Yury Kudryashov - date: 2021 + comment: We have `DiffContOnCl.circleIntegral_eq_zero` for circle contours + url: https://leanprover-community.github.io/mathlib4_docs/Mathlib/Analysis/Complex/CauchyIntegral.html#DiffContOnCl.circleIntegral_eq_zero Q834211: title: Wallace–Bolyai–Gerwien theorem @@ -1401,6 +1402,7 @@ Q1139524: Q1140119: title: Morera's theorem + decl: Complex.isConservativeOn_and_continuousOn_iff_isDifferentiableOn Q1140200: title: PCP theorem @@ -2246,6 +2248,9 @@ Q2621667: Q2631152: title: Carathéodory's extension theorem + decls: + - MeasureTheory.AddContent.measure + - MeasureTheory.AddContent.measure_eq Q2635326: title: Structured program theorem @@ -2482,7 +2487,7 @@ Q3527162: Q3527166: title: Steinhaus theorem - # not difficult, but seemingly missing + decl: MeasureTheory.Measure.div_mem_nhds_one_of_haar_pos Q3527171: title: Synge's theorem @@ -4157,6 +4162,7 @@ Q20278711: Q20971632: title: Lie's theorem + decl: LieModule.exists_nontrivial_weightSpace_of_isSolvable Q22952648: title: Uncountability of the continuum diff --git a/docs/references.bib b/docs/references.bib index 8e32df4b15ecb9..44254620fc7a89 100644 --- a/docs/references.bib +++ b/docs/references.bib @@ -1670,6 +1670,21 @@ @Book{ diamondshurman2005 zbl = {1062.11022} } +@Book{ DiestelUhl1977, + author = {Diestel, J. and Uhl, J. J. jun.}, + title = {Vector measures}, + fseries = {Mathematical Surveys}, + series = {Math. Surv.}, + issn = {0076-5376}, + volume = {15}, + year = {1977}, + publisher = {American Mathematical Society (AMS), Providence, RI}, + language = {English}, + keywords = {46G10,28B05,28A15,28A20,46-02,46B10,46B99,46E15,46E30,46G05,47A65,47B06,47B10,47B99}, + zbmath = {3576139}, + zbl = {0369.46039} +} + @Article{ dieudonne1953, author = {Dieudonn\'{e}, Jean}, title = {On semi-simple {L}ie algebras}, @@ -2144,6 +2159,17 @@ @Book{ freyd1964abelian publisher = {Harper \& Row New York} } +@Misc{ freyd1966repabelian, + author = {Freyd, P.}, + title = {Representations in {Abelian} categories}, + year = {1966}, + language = {English}, + howpublished = {Proc. {Conf}. {Categor}. {Algebra}, {La} {Jolla} 1965, + 95-120 (1966).}, + zbmath = {3321315}, + zbl = {0202.32402} +} + @Book{ friedmanscarr2005, author = {Yaakov {Friedman}}, title = {{Physical applications of homogeneous balls. With the @@ -3930,6 +3956,20 @@ @Book{ matsumura1987 doi = {https://doi.org/10.1017/CBO9781139171762} } +@Book{ matsumuraCommAlg, + author = {Matsumura, Hideyuki}, + title = {Commutative algebra. 2nd ed}, + fseries = {Mathematics Lecture Note Series}, + series = {Math. Lect. Note Ser.}, + volume = {56}, + year = {1980}, + publisher = {The Benjamin/Cummings Publishing Company, Reading, MA}, + language = {English}, + keywords = {13-02,13Cxx,13C11,13H10,13C15,13Axx}, + zbmath = {3687501}, + zbl = {0441.13001} +} + @Article{ matsuo1997, title = {On axioms for a vertex algebra and locality of quantum fields}, @@ -4761,6 +4801,21 @@ @Misc{ ponton2020chebyshev primaryclass = {math.NT} } +@Article{ posur2021Freyd, + author = {Posur, Sebastian}, + title = {A constructive approach to {Freyd} categories}, + fjournal = {Applied Categorical Structures}, + journal = {Appl. Categ. Struct.}, + issn = {0927-2852}, + volume = {29}, + number = {1}, + pages = {171--211}, + year = {2021}, + keywords = {18E10,18E05,18A25,16S99,18C35}, + zbmath = {7363561}, + zbl = {1478.18008} +} + @Article{ Prielipp1970, author = {Robert W. Prielipp}, title = {PERFECT NUMBERS, ABUNDANT NUMBERS, AND DEFICIENT NUMBERS}, @@ -5781,6 +5836,15 @@ @Article{ van_der_hoeven doi = {10.1215/ijm/1258138061} } +@Misc{ vandendries_henson_2016, + author = {van den Dries, Lou and Henson, C. Ward}, + title = {Lecture Notes for {M}athematics 571 {F}all 2016 {M}odel + {T}heory}, + year = {2016}, + institution = {Mathematics Department, University of Illinois at + Urbana-Champaign} +} + @Book{ verdier1996, author = {Verdier, Jean-Louis}, title = {Des cat\'{e}gories d\'{e}riv\'{e}es des cat\'{e}gories diff --git a/docs/undergrad.yaml b/docs/undergrad.yaml index 710b7695d179c4..9c97063d8ff3cb 100644 --- a/docs/undergrad.yaml +++ b/docs/undergrad.yaml @@ -466,7 +466,7 @@ Multivariable calculus: $k$-th order partial derivatives: '' partial derivatives commute: 'second_derivative_symmetric' Taylor's theorem with little-o remainder: '' - Taylor's theorem with integral form for remainder: '' + Taylor's theorem with integral form for remainder: 'map_add_eq_sum_add_integral_iteratedFDeriv' local extrema: 'IsLocalMin.fderiv_eq_zero' convexity of functions on an open convex subset of $\R^n$: 'ConvexOn' diffeomorphisms: 'Structomorph' diff --git a/docs/workflows.md b/docs/workflows.md index f64770727cb929..45afc95ad2e788 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -59,7 +59,7 @@ Primary trigger for this section: PR/merge-queue events (`pull_request`, `pull_r | File | Name | Importance | Triggers | Description | |---|---|---|---|---| -| [`maintainer_bors.yml`](../.github/workflows/maintainer_bors.yml) | Add "ready-to-merge" and "delegated" label
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/maintainer_bors.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors.yml) | High | `issue_comment, pull_request_review, pull_request_review_comment` | Processes bors merge/delegate commands, updates labels, and emits artifact/context for follow-up workflows. | +| [`maintainer_bors.yml`](../.github/workflows/maintainer_bors.yml) | Bors merge/delegate follow-up
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/maintainer_bors.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors.yml) | High | `issue_comment, pull_request_review, pull_request_review_comment` | Detects bors merge/delegate commands and emits a bridge artifact for the privileged follow-up workflow. The `ready-to-merge`/`delegated` lifecycle labels are managed by bors itself (see `bors.toml`). | | [`maintainer_merge.yml`](../.github/workflows/maintainer_merge.yml) | Maintainer merge
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/maintainer_merge.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_merge.yml) | High | `issue_comment, pull_request_review, pull_request_review_comment` | Handles maintainer merge/delegate commands, performs permission checks, and posts Zulip/PR notifications. | | [`labels_from_comment.yml`](../.github/workflows/labels_from_comment.yml) | Label PR based on Comment
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/labels_from_comment.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/labels_from_comment.yml) | Medium | `issue_comment, pull_request_review, pull_request_review_comment` | Adds/removes an allowlisted set of labels based on comment/review text commands. | | [`bot_fix_style.yaml`](../.github/workflows/bot_fix_style.yaml) | bot fix style
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/bot_fix_style.yaml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/bot_fix_style.yaml) | Low | `issue_comment, pull_request_review, pull_request_review_comment` | Responds to review/comment events and runs `lint-style-action` in `fix` mode. | @@ -100,8 +100,9 @@ Primary trigger for this section: completion of other workflows (`workflow_run`) |---|---|---|---|---| | [`nightly_detect_failure.yml`](../.github/workflows/nightly_detect_failure.yml) | Post to zulip if the nightly-testing branch is failing.
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/nightly_detect_failure.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/nightly_detect_failure.yml) | High | `workflow_run` | Reacts to nightly-testing CI outcomes; posts status updates and performs branch/tag maintenance on success. | | [`update_dependencies_zulip.yml`](../.github/workflows/update_dependencies_zulip.yml) | Monitor Dependency Update Failures
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/update_dependencies_zulip.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/update_dependencies_zulip.yml) | High | `workflow_run` | Watches dependency-update CI runs and sends Zulip success/failure messages with PR/label handling. | -| [`maintainer_bors_wf_run.yml`](../.github/workflows/maintainer_bors_wf_run.yml) | Add "ready-to-merge" and "delegated" label (workflow_run)
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/maintainer_bors_wf_run.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors_wf_run.yml) | Medium | `workflow_run` | Manages labels plus Zulip emoji updates for Bors commands. | +| [`maintainer_bors_wf_run.yml`](../.github/workflows/maintainer_bors_wf_run.yml) | Bors merge/delegate follow-up (workflow_run)
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/maintainer_bors_wf_run.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_bors_wf_run.yml) | Medium | `workflow_run` | Privileged companion: removes `awaiting-author`/`maintainer-merge` and updates Zulip emoji reactions on bors merge/delegate commands. The `ready-to-merge`/`delegated` lifecycle labels are managed by bors itself. | | [`maintainer_merge_wf_run.yml`](../.github/workflows/maintainer_merge_wf_run.yml) | Maintainer merge (workflow_run)
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/maintainer_merge_wf_run.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/maintainer_merge_wf_run.yml) | Medium | `workflow_run` | Manages labels and posts on Zulip for maintainer merge/delegate commands. | | [`olean_report_wf_run.yaml`](../.github/workflows/olean_report_wf_run.yaml) | olean report (workflow_run)
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/olean_report_wf_run.yaml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/olean_report_wf_run.yaml) | Low | `workflow_run` | Privileged companion to `olean_report.yaml`. Downloads the bridge artifact and posts or updates the olean diff as a comment on the PR. | | [`decls-diff.yml`](../.github/workflows/decls-diff.yml) | Declarations diff (post-build)
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/decls-diff.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/decls-diff.yml) | Low | `workflow_run` | Post-build companion to `ci` that diffs the `import-graph` artifact of a PR build against its master merge-base and patches the `### PR summary` comment's declarations-diff section with the Lean-aware result (or a cache-miss notice). | | [`export_telemetry.yaml`](../.github/workflows/export_telemetry.yaml) | Export workflow telemetry
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/export_telemetry.yaml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/export_telemetry.yaml) | Low | `workflow_run` | Exports CI run telemetry to OTLP when selected CI workflows complete. | +| [`export_crossrefs.yml`](../.github/workflows/export_crossrefs.yml) | Export cross-reference data
[![passed/failed](https://img.shields.io/github/actions/workflow/status/leanprover-community/mathlib4/export_crossrefs.yml?label=status)](https://github.com/leanprover-community/mathlib4/actions/workflows/export_crossrefs.yml) | Low | `workflow_run` | After a successful master `ci` build, regenerates the `@[wikidata]`/`@[stacks]`/`@[kerodon]` cross-reference JSON via `scripts/export_crossrefs.lean` and pushes it to the versioned crossrefs repository when the entries change. | diff --git a/lake-manifest.json b/lake-manifest.json index aff8d1dac87c1c..25f55d0dc1c353 100644 --- a/lake-manifest.json +++ b/lake-manifest.json @@ -5,7 +5,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "63045536fe95024e6c18fc7b48e03f506701c5bc", + "rev": "f3c7bd5061bd81b4480295c524d4f245c8b7e4e2", "name": "plausible", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -25,7 +25,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "5c7542ed018c78194f1e2b903eaf6a792b74c03d", + "rev": "41f407a8e85b0fdc00910633a8f14754139b63f4", "name": "importGraph", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -35,7 +35,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "24b0d9dc081c5423f8eec7e866c441e5184f29d9", + "rev": "e4952ae2afde0dd2868b313356d1ce00da677bd9", "name": "proofwidgets", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -45,7 +45,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "e3cb2f741431ce31bf73549fb52316a57368b06f", + "rev": "b5b9e2bb45ce91e4bc44eaa738c3a8910404ab82", "name": "aesop", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -55,7 +55,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "f46324995fca5f0483b742e4eb4daec7f4ee50d2", + "rev": "7a62bd13860cd39ac98da16ffc8c24d601353f69", "name": "Qq", "manifestFile": "lake-manifest.json", "inputRev": "master", @@ -65,7 +65,7 @@ "type": "git", "subDir": null, "scope": "leanprover-community", - "rev": "fc38104235ab6cf8a448a74405aa258804ef4e36", + "rev": "9c6e03c0a86237b199ebe6b1fbac18078a767ade", "name": "batteries", "manifestFile": "lake-manifest.json", "inputRev": "main", @@ -75,12 +75,12 @@ "type": "git", "subDir": null, "scope": "leanprover", - "rev": "92564e5770e4d09f2d86dfbf8ada1e9c715b384c", + "rev": "406ebb8c8e2f7e852a1b47764b42494022ce652c", "name": "Cli", "manifestFile": "lake-manifest.json", - "inputRev": "v4.31.0", + "inputRev": "v4.32.0-rc1", "inherited": true, "configFile": "lakefile.toml"}], "name": "mathlib", "lakeDir": ".lake", - "fixedToolchain": false} + "fixedToolchain": true} diff --git a/lakefile.lean b/lakefile.lean index 46071184a166ee..a4808f93fdd2c0 100644 --- a/lakefile.lean +++ b/lakefile.lean @@ -50,6 +50,12 @@ package mathlib where testDriver := "MathlibTest" lintDriver := "batteries/runLinter" lintDriverArgs := #["Mathlib"] + -- A version of Mathlib only supports the toolchain it is built with. + fixedToolchain := true + -- Mathlib oleans are built on Linux CI and used across platforms. + platformIndependent := true + -- Mathlib currently expects artifacts to be in the build directory. + restoreAllArtifacts := true -- These are additional settings which do not affect the lake hash, -- so they can be enabled in CI and disabled locally or vice versa. -- Warning: Do not put any options here that actually change the olean files, diff --git a/lean-toolchain b/lean-toolchain index 18640c8b066b18..2694eb767c1d5d 100644 --- a/lean-toolchain +++ b/lean-toolchain @@ -1 +1 @@ -leanprover/lean4:v4.31.0 +leanprover/lean4:v4.32.0-rc1 diff --git a/scripts/README.md b/scripts/README.md index c8a19d945775d8..f41ad778e88a4e 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -236,6 +236,17 @@ to module `Foo.Bar` (no `srcDir` indirection). normalize the BibTeX file `docs/references.bib` using `bibtool`. - `yaml_check.py`, `check-yaml.lean` Sanity checks for `undergrad.yaml`, `overview.yaml`, `100.yaml` and `1000.yaml`. +- `export_crossrefs.lean` + Exports a JSON dictionary of every declaration tagged with `@[wikidata]`, `@[stacks]`, or + `@[kerodon]` (declaration name, source file, line number, and the cross-reference ids). + It runs as a Lean command over the fully-imported `Mathlib` environment (like `#stacks_tags`), + so it is invoked with `lake env lean scripts/export_crossrefs.lean` rather than `lake exe`. + The output path defaults to `crossrefs.json` (override with `CROSSREFS_OUT`); the embedded + mathlib commit SHA is read from `CROSSREFS_COMMIT`. The + [`export_crossrefs.yml`](../.github/workflows/export_crossrefs.yml) workflow runs this after every + successful master build and publishes the result to the + [`crossref-exports`](https://github.com/leanprover-community/crossref-exports) repository + (committing only when the entries actually change). - `autolabel.lean` is the Lean script in charge of automatically adding a `t-`label on eligible PRs. Autolabelling is inferred by which directories the current PR modifies. - `auto_commit.sh` runs a command and creates a commit with the result. The commit message format diff --git a/scripts/create_deprecated_modules.lean b/scripts/create_deprecated_modules.lean index 31705550029ad0..94f0be436a5645 100644 --- a/scripts/create_deprecated_modules.lean +++ b/scripts/create_deprecated_modules.lean @@ -50,13 +50,19 @@ It returns just the imports of `fileContent`, including trailing comments if `ke -/ def getHeader (fname fileContent : String) (keepTrailing : Bool) : IO String := do let (stx, _) ← Parser.parseHeader (Parser.mkInputContext fileContent fname) + -- The first argument is a module token (if any). + let isModule := !(stx.raw.getArg 0).getArgs.isEmpty let imports := stx.raw.getArg 2 -- extract just the imports list let imports := if keepTrailing then imports else imports.unsetTrailing -- Use `withLeading := false` to exclude leading comments (e.g. copyright header) that the -- parser now attaches to the first token (see leanprover/lean4#12662). let some substring := imports.getSubstring? (withLeading := false) | throw <| .userError "No substring: we have a problem!" - return substring.toString + -- This implementation is geared for to create a deprecated module: + -- since imports there are unused by definition, we need to tell shake to keep them. + -- For a general-purpose implementation, whether to add the `--keep-all` flag + -- should be made configurable. + return (if isModule then "module -- shake: keep-all\n\n" else "") ++ substring.toString /-- `getHeaderFromFileName fname keepTrailing` is similar to `getHeader`, except that it assumes that @@ -85,7 +91,7 @@ def mkDeprecationWithDate (date : String) def mkDeprecation (customMessage : Option String := some "Auto-generated deprecation") : CommandElabM Format := do -- Get the current date in UTC: we don't want this to depend on the user computer's time zone. - let date := s!"{(← Std.Time.DateTime.now (tz := .UTC)).toPlainDate}" + let date := s!"{(Std.Time.DateTime.ofTimestamp (← Std.Time.Timestamp.now) .UTC).toPlainDate}" mkDeprecationWithDate date customMessage /-- @@ -161,6 +167,8 @@ def mkRenamesDict (percent : Nat := 100) : IO (Std.HashMap String String) := do and a similarity percentage.\nFull git line: '{git}'" continue let some pctNat := (pct.drop 1).toNat? | continue + -- We skip renames of files in `MathlibTest`. + if oldName.startsWith "MathlibTest/" then continue -- This looks like a rename with a similarity index at least as big as our threshold: -- we add the rename to our dictionary. if percent ≤ pctNat then @@ -233,10 +241,11 @@ def deprecateFilePath (fname : String) (rename comment : Option String) : -- Retrieve the final version of the file, before it was deleted. let file ← runCmd s!"git show {modifiedHash}:{fname}" -- Generate a module deprecation for the file `fname`. + -- As mathlib uses the module system, it is fine to always generate a module. let fileHeader ← match rename with | some rename => do let modName := mkModName rename - pure s!"import {modName}" + pure s!"module -- shake: keep-all\n\npublic import {modName}" | none => getHeader fname file false let deprecatedFile := s!"{fileHeader.trimAsciiEnd}\n\n{deprecation.pretty.trimAsciiEnd}\n" msgs := msgs.push <| .trace {cls := `Deprecation} m!"{fname}" #[m!"\n{deprecatedFile}"] diff --git a/scripts/downstream_repos.yml b/scripts/downstream_repos.yml index 2c29d75c188d29..419a9addf503af 100644 --- a/scripts/downstream_repos.yml +++ b/scripts/downstream_repos.yml @@ -92,6 +92,15 @@ build: push.yml docs: push.yml release-tag: lean-release-tag.yml +- github: https://github.com/LeanMachineLearning/LML + default_branch: main + name: Lean Machine Learning + zulip-contact: Rémy Degenne + workflows: + build: blueprint.yml + docs: blueprint.yml + release-tag: create-release.yml + update: update.yml - github: https://github.com/RemyDegenne/brownian-motion default_branch: master name: Brownian motion project diff --git a/scripts/export_crossrefs.lean b/scripts/export_crossrefs.lean new file mode 100644 index 00000000000000..724face882f1b4 --- /dev/null +++ b/scripts/export_crossrefs.lean @@ -0,0 +1,73 @@ +/- +Copyright (c) 2026 Johan Commelin. All rights reserved. +Released under Apache 2.0 license as described in the file LICENSE. +Authors: Johan Commelin +-/ + +import Mathlib + +/-! +# Export cross-reference data as JSON + +Elaborating this file writes a JSON file listing every Mathlib declaration tagged with +`@[wikidata ...]`, `@[stacks ...]`, or `@[kerodon ...]`, together with its source file, line +number, and the referenced identifiers. + +The cross-references are read from the ambient environment (like the `#stacks_tags` command), so +the file must be run with the full `Mathlib` import elaborated: + + lake env lean scripts/export_crossrefs.lean + +The output path is taken from the `CROSSREFS_OUT` environment variable (default `crossrefs.json`) +and the recorded source commit from `CROSSREFS_COMMIT` (default `unknown`). +-/ + +open Lean Mathlib.CrossRef + +namespace ExportCrossRefs + +/-- The name of the module containing `decl`, and the 1-based line number where `decl` is +declared, if known. -/ +def declLocation (env : Environment) (decl : Name) : Option (String × Nat) := do + -- Inline `findDeclarationRangesCore?` since it's monadic. + let ranges ← declRangeExt.find? (level := .exported) env decl <|> + declRangeExt.find? (level := .server) env decl + let mod ← env.getModuleFor? decl + return (mod.toString, ranges.selectionRange.pos.line) + +/-- One JSON entry per declaration, sorted by declaration name. A declaration carrying several +cross-references (e.g. both a Stacks and a Wikidata tag) gets a single entry with multiple `refs`. -/ +def buildEntries (env : Environment) : Array Json := Id.run do + let mut byDecl : Std.HashMap Name (Array Tag) := {} + let (localTags, importedTags) := PersistentEnvExtension.getState tagExt env + for tags in importedTags do + for tag in tags do + byDecl := byDecl.alter tag.declName fun val? => val?.getD #[] |>.push tag + for tag in localTags do + byDecl := byDecl.alter tag.declName fun val? => val?.getD #[] |>.push tag + let sorted := byDecl.toArray.qsort fun a b => a.1.toString < b.1.toString + sorted.map fun (decl, tags) => + -- Canonical ref order (by database, then id) keeps the published file stable. + let tags := tags.qsort fun a b => + (compare a.database b.database).then (compare a.tag b.tag) |>.isLT + let refs : Array Json := tags.map fun t => + json% { db : $(t.database.shortName), id : $(t.tag), comment : $(t.comment) } + let (mod, line) := declLocation env decl |>.getD ("", 0) + json% { decl : $(decl.toString), module : $(mod), line : $(line), refs : $(refs) } + +end ExportCrossRefs + +open ExportCrossRefs in +-- TODO: consider moving to `importModules`, or managing with `lake` +-- (see also environment linter internals, which may eventually do the latter) +run_cmd do + let entries := buildEntries (← getEnv) + let now := (Std.Time.DateTime.ofTimestamp (← Std.Time.Timestamp.now) .UTC).toISO8601String + let json := json% { + generated : $(now.trimAscii.toString), + commit : $((← IO.getEnv "CROSSREFS_COMMIT").getD "unknown"), + entries : $(entries) + } + let path := (← IO.getEnv "CROSSREFS_OUT").getD "crossrefs.json" + IO.FS.writeFile path (json.pretty ++ "\n") + logInfo m!"Wrote {entries.size} cross-reference entries to {path}" diff --git a/scripts/lint-style.lean b/scripts/lint-style.lean index 727dcd1c7dd608..693913299410b9 100644 --- a/scripts/lint-style.lean +++ b/scripts/lint-style.lean @@ -54,7 +54,7 @@ instance : ToExpr LinterSets := inferInstanceAs <| ToExpr (NameMap _) /-- Return the linter sets defined at this point of elaborating the current file. -/ elab "linter_sets%" : term => do - return toExpr <| linterSetsExt.getState (← getEnv) + return toExpr <| (linterSetsExt.getState (← getEnv)).merged end LinterSetsElab diff --git a/scripts/mk_all.lean b/scripts/mk_all.lean index 4e3e0125670610..cbd76c092b401e 100644 --- a/scripts/mk_all.lean +++ b/scripts/mk_all.lean @@ -52,7 +52,7 @@ def mkAllCLI (args : Parsed) : IO UInt32 := do -- If the package is `mathlib`, then it removes the libraries `Cache` and `MathlibTest` and it -- adds `Mathlib/Tactic`. let libs := ← match args.flag? "lib" with - | some lib => return #[lib.as! String] + | some lib => pure #[lib.as! String] | none => getLeanLibs let mut updates := 0 for d in libs.reverse do -- reverse to create `Mathlib/Tactic.lean` before `Mathlib.lean` diff --git a/scripts/nolints.json b/scripts/nolints.json index f46adf7bf9cee7..c2e26b90be252a 100644 --- a/scripts/nolints.json +++ b/scripts/nolints.json @@ -103,8 +103,6 @@ "CompactlySupportedContinuousMap.pullback_addMonoidHom"], ["defsWithUnderscore", "CompactlySupportedContinuousMap.pullback_monoidHom"], ["defsWithUnderscore", "ContDiffMapSupportedIn.of_support_subset"], - ["defsWithUnderscore", "ContRepresentation.coind₁_map"], - ["defsWithUnderscore", "ContRepresentation.coind₁_ι"], ["defsWithUnderscore", "ContinuousLinearMap.antilipschitzConstant_of_injective_of_isClosed_range"], ["defsWithUnderscore", @@ -638,7 +636,6 @@ ["docBlame", "RingQuot.toQuot"], ["docBlame", "Shrink.rec"], ["docBlame", "SlashAction.map"], - ["docBlame", "StarAlgEquiv.restrictScalars"], ["docBlame", "StarAlgHom.restrictScalars"], ["docBlame", "StateT.callCC"], ["docBlame", "StateT.mkLabel"], diff --git a/scripts/nolints_prime_decls.txt b/scripts/nolints_prime_decls.txt index f5c9c276fd3d88..ac4950dd3c6013 100644 --- a/scripts/nolints_prime_decls.txt +++ b/scripts/nolints_prime_decls.txt @@ -136,7 +136,6 @@ Algebra.TensorProduct.natCast_def' Algebra.toMatrix_lmul' AlgEquiv.apply_smulCommClass' AlgEquiv.coe_restrictScalars' -AlgEquiv.coe_ringEquiv' AlgEquiv.mk_coe' AlgHom.coe_mk' AlgHom.coe_restrictScalars' @@ -2652,7 +2651,7 @@ MeasureTheory.Measure.map_id' MeasureTheory.Measure.measurable_bind' MeasureTheory.Measure.MeasureDense.nonempty' MeasureTheory.Measure.nonpos_iff_eq_zero' -MeasureTheory.Measure.pi_noAtoms' +MeasureTheory.Measure.pi_nullSingletonClass' MeasureTheory.MeasurePreserving.integral_comp' MeasureTheory.Measure.restrict_apply₀' MeasureTheory.Measure.restrict_apply_eq_zero'